<commit_before>/* socket.cpp                                 -*- C++ -*-
   Rémi Attab (remi.attab@gmail.com), 18 Nov 2013
   FreeBSD-style copyright and disclaimer apply

   Socket abstraction implementation
*/

#include "socket.h"
#include "utils.h"

#include <array>
#include <string>
#include <cstring>
#include <cassert>
#include <netdb.h>
#include <ifaddrs.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/tcp.h>

namespace slick {

/******************************************************************************/
/* ADDRESS                                                                    */
/******************************************************************************/

Address::
Address(const std::string& hostPort)
{
    size_t pos = hostPort.find(':');
    assert(pos != std::string::npos);

    host = hostPort.substr(0, pos);
    port = stoi(hostPort.substr(pos + 1));
}

Address::
Address(struct sockaddr* addr)
{
    socklen_t addrlen;
    int family = addr->sa_family;

    if (family == AF_INET) addrlen = sizeof(struct sockaddr_in);
    else if (family == AF_INET6) addrlen = sizeof(struct sockaddr_in6);
    else assert(false);

    *this = Address(addr, addrlen);
}

Address::
Address(struct sockaddr* addr, socklen_t addrlen)
{
    std::array<char, 256> host;
    std::array<char, 256> service;

    int res = getnameinfo(
            addr, addrlen,
            host.data(), host.size(),
            service.data(), service.size(),
            NI_NUMERICHOST | NI_NUMERICSERV);
    SLICK_CHECK_ERRNO(!res, "Address.getnameinfo");

    this->host = std::string(host.data());
    this->port = atoi(service.data());
}

/******************************************************************************/
/* INTERFACE IT                                                               */
/******************************************************************************/

struct InterfaceIt
{
    InterfaceIt(const char* host, Port port) :
        first(nullptr), cur(nullptr)
    {
        struct addrinfo hints;
        std::memset(&hints, 0, sizeof hints);

        hints.ai_flags = host ? AI_PASSIVE : 0;
        hints.ai_family = AF_UNSPEC;
        hints.ai_socktype = SOCK_STREAM;

        assert(!host || port);
        std::string portStr = std::to_string(port);

        int ret = getaddrinfo(host, portStr.c_str(), &hints, &first);
        if (ret) {
            SLICK_CHECK_ERRNO(ret != EAI_SYSTEM, "InterfaceIt.getaddrinfo");
            throw std::logic_error("error: " + std::to_string(ret));
        }

        cur = first;
    }

    ~InterfaceIt()
    {
        if (first) freeaddrinfo(first);
    }

    explicit operator bool() const { return cur; }
    void operator++ () { cur = cur->ai_next; }
    void operator++ (int) { cur = cur->ai_next; }

    const struct addrinfo& operator* () const { return *cur; }
    const struct addrinfo* operator-> () const { return cur; }

private:
    struct addrinfo* first;
    struct addrinfo* cur;
};

/******************************************************************************/
/* SOCKET                                                                     */
/******************************************************************************/

Socket::
Socket(Socket&& other) noexcept : fd_(other.fd_)
{
    other.fd_ = -1;
}

Socket&
Socket::
operator=(Socket&& other) noexcept
{
    if (this == &other) return *this;

    fd_ = other.fd_;
    other.fd_ = -1;

    return *this;
}

Socket
Socket::
connect(const Address& addr)
{
    Socket socket;
    assert(addr);

    for (InterfaceIt it(addr.chost(), addr.port); it; it++) {

        int fd = ::socket(it->ai_family, it->ai_socktype | SOCK_NONBLOCK, it->ai_protocol);
        if (fd < 0) continue;

        FdGuard guard(fd);

        int ret = ::connect(fd, it->ai_addr, it->ai_addrlen);
        if (ret < 0 && errno != EINPROGRESS) continue;

        socket.fd_ = guard.release();
        break;
    }

    if (socket) socket.init();
    return std::move(socket);
}

Socket
Socket::
connect(const std::vector<Address>& addrs)
{
    for (const auto& addr : addrs) {
        Socket socket = connect(addr);
        if (socket) return std::move(socket);
    }
    return Socket();
}

Socket
Socket::
accept(int fd)
{
    Socket socket;

    struct sockaddr addr;
    socklen_t addrlen;

    socket.fd_ = accept4(fd, &addr, &addrlen, SOCK_NONBLOCK);
    if (socket.fd_ < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
        return std::move(socket);
    SLICK_CHECK_ERRNO(socket.fd_ >= 0, "Socket.accept");

    socket.init();
    return std::move(socket);
}

void
Socket::
init()
{
    int val = true;
    int ret = setsockopt(fd_, IPPROTO_TCP, TCP_NODELAY, &val, sizeof val);
    SLICK_CHECK_ERRNO(!ret, "Socket.setsockopt.TCP_NODELAY");
}

Socket::
~Socket()
{
    if (fd_ < 0) return;

    // There's no error checking because there's not much we can do if they fail
    shutdown(fd_, SHUT_RDWR);
    close(fd_);
}

int
Socket::
error() const
{
    int error = 0;
    socklen_t errlen = sizeof error;

    int ret = getsockopt(fd_, SOL_SOCKET, SO_ERROR, &error, &errlen);
    SLICK_CHECK_ERRNO(!ret, "Socket.getsockopt.error");

    return error;
}

void
Socket::
throwError() const
{
    int err = error();
    if (err) throw std::runtime_error(checkErrnoString(err, "Socket.error"));
}

/******************************************************************************/
/* PASSIVE SOCKET                                                             */
/******************************************************************************/

PassiveSockets::
PassiveSockets(Port port, int flags)
{
    for (InterfaceIt it(nullptr, port); it; it++) {

        int fd = socket(it->ai_family, it->ai_socktype | flags, it->ai_protocol);
        if (fd < 0) continue;

        FdGuard guard(fd);

        int ret = bind(fd, it->ai_addr, it->ai_addrlen);
        if (ret < 0) continue;

        ret = listen(fd, 1U << 8);
        if (ret < 0) continue;

        fds_.push_back(guard.release());
    }

    if (fds_.empty()) throw std::runtime_error("ERROR: no valid interface");
}

PassiveSockets::
~PassiveSockets()
{
    for (int fd : fds_) close(fd);
}

/******************************************************************************/
/* NETWORK INTERFACES                                                         */
/******************************************************************************/

std::vector<Address>
networkInterfaces(bool excludeLoopback)
{
    std::vector<Address> result;
    unsigned include = IFF_UP | IFF_RUNNING;
    unsigned exclude = excludeLoopback ? IFF_LOOPBACK : 0;

    struct ifaddrs* it;
    int ret = getifaddrs(&it);
    SLICK_CHECK_ERRNO(!ret, "networkInterfaces.getifaddrs");
    auto addrsGuard = guard([=] { freeifaddrs(it); });

    for(; it; it = it->ifa_next) {

        if (!it->ifa_addr) continue;
        if (it->ifa_flags & exclude) continue;
        if ((it->ifa_flags & include) != include) continue;

        int family = it->ifa_addr->sa_family;
        if (family != AF_INET && family != AF_INET6) continue;

        result.emplace_back(it->ifa_addr);
    }

    return std::move(result);
}

} // slick

<commit_msg>How the fuck did I miss this for so long?<commit_after>/* socket.cpp                                 -*- C++ -*-
   Rémi Attab (remi.attab@gmail.com), 18 Nov 2013
   FreeBSD-style copyright and disclaimer apply

   Socket abstraction implementation
*/

#include "socket.h"
#include "utils.h"
#include "lockless/tls.h"

#include <array>
#include <string>
#include <cstring>
#include <cassert>
#include <netdb.h>
#include <ifaddrs.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/tcp.h>

namespace slick {

/******************************************************************************/
/* ADDRESS                                                                    */
/******************************************************************************/

Address::
Address(const std::string& hostPort)
{
    size_t pos = hostPort.find(':');
    assert(pos != std::string::npos);

    host = hostPort.substr(0, pos);
    port = stoi(hostPort.substr(pos + 1));
}

Address::
Address(struct sockaddr* addr)
{
    socklen_t addrlen;
    int family = addr->sa_family;

    if (family == AF_INET) addrlen = sizeof(struct sockaddr_in);
    else if (family == AF_INET6) addrlen = sizeof(struct sockaddr_in6);
    else assert(false);

    *this = Address(addr, addrlen);
}

Address::
Address(struct sockaddr* addr, socklen_t addrlen)
{
    std::array<char, 256> host;
    std::array<char, 256> service;

    int res = getnameinfo(
            addr, addrlen,
            host.data(), host.size(),
            service.data(), service.size(),
            NI_NUMERICHOST | NI_NUMERICSERV);
    SLICK_CHECK_ERRNO(!res, "Address.getnameinfo");

    this->host = std::string(host.data());
    this->port = atoi(service.data());
}

/******************************************************************************/
/* INTERFACE IT                                                               */
/******************************************************************************/

struct InterfaceIt
{
    InterfaceIt(const char* host, Port port) :
        first(nullptr), cur(nullptr)
    {
        struct addrinfo hints;
        std::memset(&hints, 0, sizeof hints);

        hints.ai_flags = !host ? AI_PASSIVE : 0;
        hints.ai_family = AF_UNSPEC;
        hints.ai_socktype = SOCK_STREAM;

        assert(!host || port);
        std::string portStr = std::to_string(port);

        int ret = getaddrinfo(host, portStr.c_str(), &hints, &first);
        if (ret) {
            SLICK_CHECK_ERRNO(ret != EAI_SYSTEM, "InterfaceIt.getaddrinfo");
            throw std::logic_error("error: " + std::to_string(ret));
        }

        cur = first;
    }

    ~InterfaceIt()
    {
        if (first) freeaddrinfo(first);
    }

    explicit operator bool() const { return cur; }
    void operator++ () { cur = cur->ai_next; }
    void operator++ (int) { cur = cur->ai_next; }

    const struct addrinfo& operator* () const { return *cur; }
    const struct addrinfo* operator-> () const { return cur; }

private:
    struct addrinfo* first;
    struct addrinfo* cur;
};

/******************************************************************************/
/* SOCKET                                                                     */
/******************************************************************************/

Socket::
Socket(Socket&& other) noexcept : fd_(other.fd_)
{
    other.fd_ = -1;
}

Socket&
Socket::
operator=(Socket&& other) noexcept
{
    if (this == &other) return *this;

    fd_ = other.fd_;
    other.fd_ = -1;

    return *this;
}

Socket
Socket::
connect(const Address& addr)
{
    Socket socket;
    assert(addr);

    for (InterfaceIt it(addr.chost(), addr.port); it; it++) {

        int fd = ::socket(it->ai_family, it->ai_socktype | SOCK_NONBLOCK, it->ai_protocol);
        if (fd < 0) continue;

        FdGuard guard(fd);

        int ret = ::connect(fd, it->ai_addr, it->ai_addrlen);
        if (ret < 0 && errno != EINPROGRESS) continue;

        socket.fd_ = guard.release();
        break;
    }

    if (socket) socket.init();
    return std::move(socket);
}

Socket
Socket::
connect(const std::vector<Address>& addrs)
{
    for (const auto& addr : addrs) {
        Socket socket = connect(addr);
        if (socket) return std::move(socket);
    }
    return Socket();
}

Socket
Socket::
accept(int fd)
{
    Socket socket;

    struct sockaddr addr;
    socklen_t addrlen;

    socket.fd_ = accept4(fd, &addr, &addrlen, SOCK_NONBLOCK);
    if (socket.fd_ < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
        return std::move(socket);
    SLICK_CHECK_ERRNO(socket.fd_ >= 0, "Socket.accept");

    socket.init();
    return std::move(socket);
}

void
Socket::
init()
{
    int val = true;
    int ret = setsockopt(fd_, IPPROTO_TCP, TCP_NODELAY, &val, sizeof val);
    SLICK_CHECK_ERRNO(!ret, "Socket.setsockopt.TCP_NODELAY");
}

Socket::
~Socket()
{
    if (fd_ < 0) return;

    // There's no error checking because there's not much we can do if they fail
    shutdown(fd_, SHUT_RDWR);
    close(fd_);
}

int
Socket::
error() const
{
    int error = 0;
    socklen_t errlen = sizeof error;

    int ret = getsockopt(fd_, SOL_SOCKET, SO_ERROR, &error, &errlen);
    SLICK_CHECK_ERRNO(!ret, "Socket.getsockopt.error");

    return error;
}

void
Socket::
throwError() const
{
    int err = error();
    if (err) throw std::runtime_error(checkErrnoString(err, "Socket.error"));
}

/******************************************************************************/
/* PASSIVE SOCKET                                                             */
/******************************************************************************/

PassiveSockets::
PassiveSockets(Port port, int flags)
{
    for (InterfaceIt it(nullptr, port); it; it++) {

        int fd = socket(it->ai_family, it->ai_socktype | flags, it->ai_protocol);
        if (fd < 0) continue;

        FdGuard guard(fd);

        int ret = bind(fd, it->ai_addr, it->ai_addrlen);
        if (ret < 0) continue;

        ret = listen(fd, 1U << 8);
        if (ret < 0) continue;

        fds_.push_back(guard.release());
    }

    if (fds_.empty()) throw std::runtime_error("ERROR: no valid interface");
}

PassiveSockets::
~PassiveSockets()
{
    for (int fd : fds_) close(fd);
}

/******************************************************************************/
/* NETWORK INTERFACES                                                         */
/******************************************************************************/

std::vector<Address>
networkInterfaces(bool excludeLoopback)
{
    std::vector<Address> result;
    unsigned include = IFF_UP | IFF_RUNNING;
    unsigned exclude = excludeLoopback ? IFF_LOOPBACK : 0;

    struct ifaddrs* it;
    int ret = getifaddrs(&it);
    SLICK_CHECK_ERRNO(!ret, "networkInterfaces.getifaddrs");
    auto addrsGuard = guard([=] { freeifaddrs(it); });

    for(; it; it = it->ifa_next) {

        if (!it->ifa_addr) continue;
        if (it->ifa_flags & exclude) continue;
        if ((it->ifa_flags & include) != include) continue;

        int family = it->ifa_addr->sa_family;
        if (family != AF_INET && family != AF_INET6) continue;

        result.emplace_back(it->ifa_addr);
    }

    return std::move(result);
}

} // slick

<|endoftext|>


<commit_before>/*
 * Copyright (c) 2016, The Bifrost Authors. All rights reserved.
 * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 * * Neither the name of The Bifrost Authors nor the names of its
 *   contributors may be used to endorse or promote products derived
 *   from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <bifrost/memory.h>
#include "utils.hpp"
#include "cuda.hpp"

#include <cstdlib> // For posix_memalign
#include <cstring> // For memcpy
#include <iostream>

#define BF_IS_POW2(x) (x) && !((x) & ((x) - 1))
static_assert(BF_IS_POW2(BF_ALIGNMENT), "BF_ALIGNMENT must be a power of 2");
#undef BF_IS_POW2
//static_assert(BF_ALIGNMENT >= 8,        "BF_ALIGNMENT must be >= 8");

BFstatus bfGetSpace(const void* ptr, BFspace* space) {
#if !defined BF_CUDA_ENABLED || !BF_CUDA_ENABLED
	*space = BF_SPACE_SYSTEM;
#else
	cudaPointerAttributes ptr_attrs;
	cudaPointerGetAttributes(&ptr_attrs, ptr);
	if( ptr_attrs.isManaged ) {
		*space = BF_SPACE_CUDA_MANAGED;
	}
	else {
		switch( ptr_attrs.memoryType ) {
		case cudaMemoryTypeHost:   *space = BF_SPACE_SYSTEM; break;
		case cudaMemoryTypeDevice: *space = BF_SPACE_CUDA;   break;
		default: {
			*space = BF_SPACE_AUTO; // TODO: Any better option than this?
			return BF_STATUS_INVALID_POINTER;
		}
		}
	}
#endif
	return BF_STATUS_SUCCESS;
}

BFstatus bfMalloc(void** ptr, BFsize size, BFspace space) {
	//printf("bfMalloc(%p, %lu, %i)\n", ptr, size, space);
	void* data;
	switch( space ) {
	case BF_SPACE_SYSTEM: {
		//data = std::aligned_alloc(std::max(BF_ALIGNMENT,8), size);
		int err = ::posix_memalign((void**)&data, std::max(BF_ALIGNMENT,8), size);
		BF_ASSERT(!err, BF_STATUS_MEM_ALLOC_FAILED);
		//if( err ) data = nullptr;
		//printf("bfMalloc --> %p\n", data);
		break;
	}
#if defined BF_CUDA_ENABLED && BF_CUDA_ENABLED
	case BF_SPACE_CUDA: {
		cudaError_t err = cudaMalloc((void**)&data, size);
		BF_ASSERT(err == cudaSuccess, BF_STATUS_MEM_ALLOC_FAILED);
		break;
	}
	case BF_SPACE_CUDA_HOST: {
		unsigned flags = cudaHostAllocDefault;
		cudaError_t err = cudaHostAlloc((void**)&data, size, flags);
		BF_ASSERT(err == cudaSuccess, BF_STATUS_MEM_ALLOC_FAILED);
		break;
	}
	case BF_SPACE_CUDA_MANAGED: {
		unsigned flags = cudaMemAttachGlobal;
		cudaError_t err = cudaMallocManaged((void**)&data, size, flags);
		BF_ASSERT(err == cudaSuccess, BF_STATUS_MEM_ALLOC_FAILED);
		break;
	}
#endif
	default: BF_ASSERT(false, BF_STATUS_INVALID_ARGUMENT);
	}
	//return data;
	*ptr = data;
	return BF_STATUS_SUCCESS;
}
BFstatus bfFree(void* ptr, BFspace space) {
	BF_ASSERT(ptr, BF_STATUS_INVALID_POINTER);
	if( space == BF_SPACE_AUTO ) {
		bfGetSpace(ptr, &space);
	}
	switch( space ) {
	case BF_SPACE_SYSTEM:       ::free(ptr); break;
#if defined BF_CUDA_ENABLED && BF_CUDA_ENABLED
	case BF_SPACE_CUDA:         cudaFree(ptr); break;
	case BF_SPACE_CUDA_HOST:    cudaFreeHost(ptr); break;
	case BF_SPACE_CUDA_MANAGED: cudaFree(ptr); break;
#endif
	default: BF_ASSERT(false, BF_STATUS_INVALID_ARGUMENT);
	}
	return BF_STATUS_SUCCESS;
}
BFstatus bfMemcpy(void*       dst,
                  BFspace     dst_space,
                  const void* src,
                  BFspace     src_space,
                  BFsize      count) {
	if( count ) {
		BF_ASSERT(dst, BF_STATUS_INVALID_POINTER);
		BF_ASSERT(src, BF_STATUS_INVALID_POINTER);
#if !defined BF_CUDA_ENABLED || !BF_CUDA_ENABLED
		::memcpy(dst, src, count);
#else
		// Note: Explicitly dispatching to ::memcpy was found to be much faster
		//         than using cudaMemcpyDefault.
		if( src_space == BF_SPACE_AUTO ) bfGetSpace(src, &src_space);
		if( dst_space == BF_SPACE_AUTO ) bfGetSpace(dst, &dst_space);
		cudaMemcpyKind kind = cudaMemcpyDefault;
		switch( src_space ) {
		case BF_SPACE_SYSTEM: {
			switch( dst_space ) {
			case BF_SPACE_CUDA_HOST: // fall-through
			case BF_SPACE_SYSTEM: ::memcpy(dst, src, count); return BF_STATUS_SUCCESS;
			case BF_SPACE_CUDA: kind = cudaMemcpyHostToDevice; break;
			// TODO: BF_SPACE_CUDA_MANAGED
			default: BF_ASSERT(false, BF_STATUS_INVALID_ARGUMENT);
			}
			break;
		}
		case BF_SPACE_CUDA: {
			switch( dst_space ) {
			case BF_SPACE_CUDA_HOST: // fall-through
			case BF_SPACE_SYSTEM: kind = cudaMemcpyDeviceToHost; break;
			case BF_SPACE_CUDA:   kind = cudaMemcpyDeviceToDevice; break;
			// TODO: BF_SPACE_CUDA_MANAGED
			default: BF_ASSERT(false, BF_STATUS_INVALID_ARGUMENT);
			}
			break;
		}
		default: BF_ASSERT(false, BF_STATUS_INVALID_ARGUMENT);
		}
		if( cudaMemcpyAsync(dst, src, count, kind, g_cuda_stream) != cudaSuccess ) {
			BF_ASSERT(false, BF_STATUS_MEM_OP_FAILED);
		}
#endif
	}
	return BF_STATUS_SUCCESS;
}
void memcpy2D(void*       dst,
              BFsize      dst_stride,
              const void* src,
              BFsize      src_stride,
              BFsize      width,
              BFsize      height) {
	//std::cout << "memcpy2D dst: " << dst << ", " << dst_stride << std::endl;
	//std::cout << "memcpy2D src: " << src << ", " << src_stride << std::endl;
	//std::cout << "memcpy2D shp: " << width << ", " << height << std::endl;
	for( BFsize row=0; row<height; ++row ) {
		::memcpy((char*)dst + row*dst_stride,
		         (char*)src + row*src_stride,
		         width);
	}
}
BFstatus bfMemcpy2D(void*       dst,
                    BFsize      dst_stride,
                    BFspace     dst_space,
                    const void* src,
                    BFsize      src_stride,
                    BFspace     src_space,
                    BFsize      width,
                    BFsize      height) {
	if( width*height ) {
		BF_ASSERT(dst, BF_STATUS_INVALID_POINTER);
		BF_ASSERT(src, BF_STATUS_INVALID_POINTER);
#if !defined BF_CUDA_ENABLED || !BF_CUDA_ENABLED
		memcpy2D(dst, dst_stride, src, src_stride, width, height);
#else
		// Note: Explicitly dispatching to ::memcpy was found to be much faster
		//         than using cudaMemcpyDefault.
		if( src_space == BF_SPACE_AUTO ) bfGetSpace(src, &src_space);
		if( dst_space == BF_SPACE_AUTO ) bfGetSpace(dst, &dst_space);
		cudaMemcpyKind kind = cudaMemcpyDefault;
		switch( src_space ) {
		case BF_SPACE_SYSTEM: {
			switch( dst_space ) {
			case BF_SPACE_CUDA_HOST: // fall-through
			case BF_SPACE_SYSTEM: memcpy2D(dst, dst_stride, src, src_stride, width, height); return BF_STATUS_SUCCESS;
			case BF_SPACE_CUDA: kind = cudaMemcpyHostToDevice; break;
			// TODO: Is this the right thing to do?
			case BF_SPACE_CUDA_MANAGED: kind = cudaMemcpyDefault; break;
			default: BF_ASSERT(false, BF_STATUS_INVALID_ARGUMENT);
			}
			break;
		}
		case BF_SPACE_CUDA: {
			switch( dst_space ) {
			case BF_SPACE_CUDA_HOST: // fall-through
			case BF_SPACE_SYSTEM: kind = cudaMemcpyDeviceToHost; break;
			case BF_SPACE_CUDA:   kind = cudaMemcpyDeviceToDevice; break;
			// TODO: Is this the right thing to do?
			case BF_SPACE_CUDA_MANAGED: kind = cudaMemcpyDefault; break;
			default: BF_ASSERT(false, BF_STATUS_INVALID_ARGUMENT);
			}
			break;
		}
		default: BF_ASSERT(false, BF_STATUS_INVALID_ARGUMENT);
		}
		if( cudaMemcpy2DAsync(dst, dst_stride,
		                      src, src_stride,
		                      width, height,
		                      kind, g_cuda_stream) != cudaSuccess ) {
			BF_ASSERT(false, BF_STATUS_MEM_OP_FAILED);
		}
#endif
	}
	return BF_STATUS_SUCCESS;
}
BFstatus bfMemset(void*   ptr,
                  BFspace space,
                  int     value,
                  BFsize  count) {
	BF_ASSERT(ptr, BF_STATUS_INVALID_POINTER);
	if( count ) {
		if( space == BF_SPACE_AUTO ) {
			// TODO: Check status here
			bfGetSpace(ptr, &space);
		}
		switch( space ) {
		case BF_SPACE_SYSTEM:       ::memset(ptr, value, count); break;
#if defined BF_CUDA_ENABLED && BF_CUDA_ENABLED
		case BF_SPACE_CUDA_HOST:    ::memset(ptr, value, count); break;
		case BF_SPACE_CUDA:
		case BF_SPACE_CUDA_MANAGED: {
			cudaMemsetAsync(ptr, value, count, g_cuda_stream);
			break;
		}
#endif
		default: BF_ASSERT(false, BF_STATUS_INVALID_ARGUMENT);
		}
	}
	return BF_STATUS_SUCCESS;
}
void memset2D(void*  ptr,
              BFsize stride,
              int    value,
              BFsize width,
              BFsize height) {
	for( BFsize row=0; row<height; ++row ) {
		::memset((char*)ptr + row*stride, value, width);
	}
}
BFstatus bfMemset2D(void*   ptr,
                    BFsize  stride,
                    BFspace space,
                    int     value,
                    BFsize  width,
                    BFsize  height) {
	BF_ASSERT(ptr, BF_STATUS_INVALID_POINTER);
	if( width*height ) {
		if( space == BF_SPACE_AUTO ) {
			bfGetSpace(ptr, &space);
		}
		switch( space ) {
		case BF_SPACE_SYSTEM:       memset2D(ptr, stride, value, width, height); break;
#if defined BF_CUDA_ENABLED && BF_CUDA_ENABLED
		case BF_SPACE_CUDA_HOST:    memset2D(ptr, stride, value, width, height); break;
		case BF_SPACE_CUDA:
		case BF_SPACE_CUDA_MANAGED: {
			cudaMemset2DAsync(ptr, stride, value, width, height, g_cuda_stream);
			break;
		}
#endif
		default: BF_ASSERT(false, BF_STATUS_INVALID_ARGUMENT);
		}
	}
	return BF_STATUS_SUCCESS;
}
BFsize bfGetAlignment() {
	return BF_ALIGNMENT;
}
<commit_msg>Fixed bfGetSpace to support non-CUDA allocations<commit_after>/*
 * Copyright (c) 2016, The Bifrost Authors. All rights reserved.
 * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 * * Neither the name of The Bifrost Authors nor the names of its
 *   contributors may be used to endorse or promote products derived
 *   from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <bifrost/memory.h>
#include "utils.hpp"
#include "cuda.hpp"

#include <cstdlib> // For posix_memalign
#include <cstring> // For memcpy
#include <iostream>

#define BF_IS_POW2(x) (x) && !((x) & ((x) - 1))
static_assert(BF_IS_POW2(BF_ALIGNMENT), "BF_ALIGNMENT must be a power of 2");
#undef BF_IS_POW2
//static_assert(BF_ALIGNMENT >= 8,        "BF_ALIGNMENT must be >= 8");

BFstatus bfGetSpace(const void* ptr, BFspace* space) {
	BF_ASSERT(ptr, BF_STATUS_INVALID_POINTER);
#if !defined BF_CUDA_ENABLED || !BF_CUDA_ENABLED
	*space = BF_SPACE_SYSTEM;
#else
	cudaPointerAttributes ptr_attrs;
	cudaError_t ret = cudaPointerGetAttributes(&ptr_attrs, ptr);
	BF_ASSERT(ret == cudaSuccess || ret == cudaErrorInvalidValue,
	          BF_STATUS_DEVICE_ERROR);
	if( ret == cudaErrorInvalidValue ) {
		// Note: cudaPointerGetAttributes only works for memory allocated with
		//         CUDA API functions, so if it fails we just assume sysmem.
		*space = BF_SPACE_SYSTEM;
	} else if( ptr_attrs.isManaged ) {
		*space = BF_SPACE_CUDA_MANAGED;
	} else {
		switch( ptr_attrs.memoryType ) {
		case cudaMemoryTypeHost:   *space = BF_SPACE_SYSTEM; break;
		case cudaMemoryTypeDevice: *space = BF_SPACE_CUDA;   break;
		default: {
			// This should never be reached
			BF_ASSERT(false, BF_STATUS_INTERNAL_ERROR);
		}
		}
	}
#endif
	return BF_STATUS_SUCCESS;
}

BFstatus bfMalloc(void** ptr, BFsize size, BFspace space) {
	//printf("bfMalloc(%p, %lu, %i)\n", ptr, size, space);
	void* data;
	switch( space ) {
	case BF_SPACE_SYSTEM: {
		//data = std::aligned_alloc(std::max(BF_ALIGNMENT,8), size);
		int err = ::posix_memalign((void**)&data, std::max(BF_ALIGNMENT,8), size);
		BF_ASSERT(!err, BF_STATUS_MEM_ALLOC_FAILED);
		//if( err ) data = nullptr;
		//printf("bfMalloc --> %p\n", data);
		break;
	}
#if defined BF_CUDA_ENABLED && BF_CUDA_ENABLED
	case BF_SPACE_CUDA: {
		cudaError_t err = cudaMalloc((void**)&data, size);
		BF_ASSERT(err == cudaSuccess, BF_STATUS_MEM_ALLOC_FAILED);
		break;
	}
	case BF_SPACE_CUDA_HOST: {
		unsigned flags = cudaHostAllocDefault;
		cudaError_t err = cudaHostAlloc((void**)&data, size, flags);
		BF_ASSERT(err == cudaSuccess, BF_STATUS_MEM_ALLOC_FAILED);
		break;
	}
	case BF_SPACE_CUDA_MANAGED: {
		unsigned flags = cudaMemAttachGlobal;
		cudaError_t err = cudaMallocManaged((void**)&data, size, flags);
		BF_ASSERT(err == cudaSuccess, BF_STATUS_MEM_ALLOC_FAILED);
		break;
	}
#endif
	default: BF_ASSERT(false, BF_STATUS_INVALID_ARGUMENT);
	}
	//return data;
	*ptr = data;
	return BF_STATUS_SUCCESS;
}
BFstatus bfFree(void* ptr, BFspace space) {
	BF_ASSERT(ptr, BF_STATUS_INVALID_POINTER);
	if( space == BF_SPACE_AUTO ) {
		bfGetSpace(ptr, &space);
	}
	switch( space ) {
	case BF_SPACE_SYSTEM:       ::free(ptr); break;
#if defined BF_CUDA_ENABLED && BF_CUDA_ENABLED
	case BF_SPACE_CUDA:         cudaFree(ptr); break;
	case BF_SPACE_CUDA_HOST:    cudaFreeHost(ptr); break;
	case BF_SPACE_CUDA_MANAGED: cudaFree(ptr); break;
#endif
	default: BF_ASSERT(false, BF_STATUS_INVALID_ARGUMENT);
	}
	return BF_STATUS_SUCCESS;
}
BFstatus bfMemcpy(void*       dst,
                  BFspace     dst_space,
                  const void* src,
                  BFspace     src_space,
                  BFsize      count) {
	if( count ) {
		BF_ASSERT(dst, BF_STATUS_INVALID_POINTER);
		BF_ASSERT(src, BF_STATUS_INVALID_POINTER);
#if !defined BF_CUDA_ENABLED || !BF_CUDA_ENABLED
		::memcpy(dst, src, count);
#else
		// Note: Explicitly dispatching to ::memcpy was found to be much faster
		//         than using cudaMemcpyDefault.
		if( src_space == BF_SPACE_AUTO ) bfGetSpace(src, &src_space);
		if( dst_space == BF_SPACE_AUTO ) bfGetSpace(dst, &dst_space);
		cudaMemcpyKind kind = cudaMemcpyDefault;
		switch( src_space ) {
		case BF_SPACE_SYSTEM: {
			switch( dst_space ) {
			case BF_SPACE_CUDA_HOST: // fall-through
			case BF_SPACE_SYSTEM: ::memcpy(dst, src, count); return BF_STATUS_SUCCESS;
			case BF_SPACE_CUDA: kind = cudaMemcpyHostToDevice; break;
			// TODO: BF_SPACE_CUDA_MANAGED
			default: BF_ASSERT(false, BF_STATUS_INVALID_ARGUMENT);
			}
			break;
		}
		case BF_SPACE_CUDA: {
			switch( dst_space ) {
			case BF_SPACE_CUDA_HOST: // fall-through
			case BF_SPACE_SYSTEM: kind = cudaMemcpyDeviceToHost; break;
			case BF_SPACE_CUDA:   kind = cudaMemcpyDeviceToDevice; break;
			// TODO: BF_SPACE_CUDA_MANAGED
			default: BF_ASSERT(false, BF_STATUS_INVALID_ARGUMENT);
			}
			break;
		}
		default: BF_ASSERT(false, BF_STATUS_INVALID_ARGUMENT);
		}
		if( cudaMemcpyAsync(dst, src, count, kind, g_cuda_stream) != cudaSuccess ) {
			BF_ASSERT(false, BF_STATUS_MEM_OP_FAILED);
		}
#endif
	}
	return BF_STATUS_SUCCESS;
}
void memcpy2D(void*       dst,
              BFsize      dst_stride,
              const void* src,
              BFsize      src_stride,
              BFsize      width,
              BFsize      height) {
	//std::cout << "memcpy2D dst: " << dst << ", " << dst_stride << std::endl;
	//std::cout << "memcpy2D src: " << src << ", " << src_stride << std::endl;
	//std::cout << "memcpy2D shp: " << width << ", " << height << std::endl;
	for( BFsize row=0; row<height; ++row ) {
		::memcpy((char*)dst + row*dst_stride,
		         (char*)src + row*src_stride,
		         width);
	}
}
BFstatus bfMemcpy2D(void*       dst,
                    BFsize      dst_stride,
                    BFspace     dst_space,
                    const void* src,
                    BFsize      src_stride,
                    BFspace     src_space,
                    BFsize      width,
                    BFsize      height) {
	if( width*height ) {
		BF_ASSERT(dst, BF_STATUS_INVALID_POINTER);
		BF_ASSERT(src, BF_STATUS_INVALID_POINTER);
#if !defined BF_CUDA_ENABLED || !BF_CUDA_ENABLED
		memcpy2D(dst, dst_stride, src, src_stride, width, height);
#else
		// Note: Explicitly dispatching to ::memcpy was found to be much faster
		//         than using cudaMemcpyDefault.
		if( src_space == BF_SPACE_AUTO ) bfGetSpace(src, &src_space);
		if( dst_space == BF_SPACE_AUTO ) bfGetSpace(dst, &dst_space);
		cudaMemcpyKind kind = cudaMemcpyDefault;
		switch( src_space ) {
		case BF_SPACE_SYSTEM: {
			switch( dst_space ) {
			case BF_SPACE_CUDA_HOST: // fall-through
			case BF_SPACE_SYSTEM: memcpy2D(dst, dst_stride, src, src_stride, width, height); return BF_STATUS_SUCCESS;
			case BF_SPACE_CUDA: kind = cudaMemcpyHostToDevice; break;
			// TODO: Is this the right thing to do?
			case BF_SPACE_CUDA_MANAGED: kind = cudaMemcpyDefault; break;
			default: BF_ASSERT(false, BF_STATUS_INVALID_ARGUMENT);
			}
			break;
		}
		case BF_SPACE_CUDA: {
			switch( dst_space ) {
			case BF_SPACE_CUDA_HOST: // fall-through
			case BF_SPACE_SYSTEM: kind = cudaMemcpyDeviceToHost; break;
			case BF_SPACE_CUDA:   kind = cudaMemcpyDeviceToDevice; break;
			// TODO: Is this the right thing to do?
			case BF_SPACE_CUDA_MANAGED: kind = cudaMemcpyDefault; break;
			default: BF_ASSERT(false, BF_STATUS_INVALID_ARGUMENT);
			}
			break;
		}
		default: BF_ASSERT(false, BF_STATUS_INVALID_ARGUMENT);
		}
		if( cudaMemcpy2DAsync(dst, dst_stride,
		                      src, src_stride,
		                      width, height,
		                      kind, g_cuda_stream) != cudaSuccess ) {
			BF_ASSERT(false, BF_STATUS_MEM_OP_FAILED);
		}
#endif
	}
	return BF_STATUS_SUCCESS;
}
BFstatus bfMemset(void*   ptr,
                  BFspace space,
                  int     value,
                  BFsize  count) {
	BF_ASSERT(ptr, BF_STATUS_INVALID_POINTER);
	if( count ) {
		if( space == BF_SPACE_AUTO ) {
			// TODO: Check status here
			bfGetSpace(ptr, &space);
		}
		switch( space ) {
		case BF_SPACE_SYSTEM:       ::memset(ptr, value, count); break;
#if defined BF_CUDA_ENABLED && BF_CUDA_ENABLED
		case BF_SPACE_CUDA_HOST:    ::memset(ptr, value, count); break;
		case BF_SPACE_CUDA:
		case BF_SPACE_CUDA_MANAGED: {
			cudaMemsetAsync(ptr, value, count, g_cuda_stream);
			break;
		}
#endif
		default: BF_ASSERT(false, BF_STATUS_INVALID_ARGUMENT);
		}
	}
	return BF_STATUS_SUCCESS;
}
void memset2D(void*  ptr,
              BFsize stride,
              int    value,
              BFsize width,
              BFsize height) {
	for( BFsize row=0; row<height; ++row ) {
		::memset((char*)ptr + row*stride, value, width);
	}
}
BFstatus bfMemset2D(void*   ptr,
                    BFsize  stride,
                    BFspace space,
                    int     value,
                    BFsize  width,
                    BFsize  height) {
	BF_ASSERT(ptr, BF_STATUS_INVALID_POINTER);
	if( width*height ) {
		if( space == BF_SPACE_AUTO ) {
			bfGetSpace(ptr, &space);
		}
		switch( space ) {
		case BF_SPACE_SYSTEM:       memset2D(ptr, stride, value, width, height); break;
#if defined BF_CUDA_ENABLED && BF_CUDA_ENABLED
		case BF_SPACE_CUDA_HOST:    memset2D(ptr, stride, value, width, height); break;
		case BF_SPACE_CUDA:
		case BF_SPACE_CUDA_MANAGED: {
			cudaMemset2DAsync(ptr, stride, value, width, height, g_cuda_stream);
			break;
		}
#endif
		default: BF_ASSERT(false, BF_STATUS_INVALID_ARGUMENT);
		}
	}
	return BF_STATUS_SUCCESS;
}
BFsize bfGetAlignment() {
	return BF_ALIGNMENT;
}
<|endoftext|>


<commit_before>#include "ncurses.hh"

#include "display_buffer.hh"
#include "register_manager.hh"

#include "utf8_iterator.hh"

#include <map>

#include <signal.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <fcntl.h>

namespace Kakoune
{

static void set_attribute(int attribute, bool on)
{
    if (on)
        attron(attribute);
    else
        attroff(attribute);
}

static int nc_color(Color color)
{
    switch (color)
    {
    case Color::Black:   return COLOR_BLACK;
    case Color::Red:     return COLOR_RED;
    case Color::Green:   return COLOR_GREEN;
    case Color::Yellow:  return COLOR_YELLOW;
    case Color::Blue:    return COLOR_BLUE;
    case Color::Magenta: return COLOR_MAGENTA;
    case Color::Cyan:    return COLOR_CYAN;
    case Color::White:   return COLOR_WHITE;

    case Color::Default:
    default:
        return -1;
    }
}

static int get_color_pair(Color fg_color, Color bg_color)
{
    static std::map<std::pair<Color, Color>, int> colorpairs;
    static int next_pair = 1;

    std::pair<Color, Color> colorpair(fg_color, bg_color);

    auto it = colorpairs.find(colorpair);
    if (it != colorpairs.end())
        return it->second;
    else
    {
        init_pair(next_pair, nc_color(fg_color), nc_color(bg_color));
        colorpairs[colorpair] = next_pair;
        return next_pair++;
    }
}

static void set_color(Color fg_color, Color bg_color)
{
    static int current_pair = -1;

    if (current_pair != -1)
        attroff(COLOR_PAIR(current_pair));

    if (fg_color != Color::Default or bg_color != Color::Default)
    {
        current_pair = get_color_pair(fg_color, bg_color);
        attron(COLOR_PAIR(current_pair));
    }
}

static NCursesUI* signal_ui;
void on_term_resize(int)
{
    int fd = open("/dev/tty", O_RDWR);
    winsize ws;
    if (fd == 0 or ioctl(fd, TIOCGWINSZ, (void*)&ws) != 0)
        return;
    close(fd);
    resizeterm(ws.ws_row, ws.ws_col);
    ungetch(KEY_RESIZE);
    signal_ui->update_dimensions();
}

NCursesUI::NCursesUI()
{
    //setlocale(LC_CTYPE, "");
    initscr();
    cbreak();
    noecho();
    nonl();
    intrflush(stdscr, false);
    keypad(stdscr, true);
    curs_set(0);
    start_color();
    use_default_colors();
    ESCDELAY=25;

    m_menu_fg = get_color_pair(Color::Blue, Color::Cyan);
    m_menu_bg = get_color_pair(Color::Cyan, Color::Blue);

    update_dimensions();

    signal_ui = this;
    signal(SIGWINCH, on_term_resize);
}

NCursesUI::~NCursesUI()
{
    endwin();
}

static void redraw(WINDOW* menu_win)
{
    wnoutrefresh(stdscr);
    if (menu_win)
    {
        redrawwin(menu_win);
        wnoutrefresh(menu_win);
    }
    doupdate();
}
using Utf8Policy = utf8::InvalidBytePolicy::Pass;
using Utf8Iterator = utf8::utf8_iterator<String::iterator, Utf8Policy>;
void addutf8str(Utf8Iterator begin, Utf8Iterator end)
{
    while (begin != end)
        addch(*begin++);
}

void NCursesUI::update_dimensions()
{
    int max_x,max_y;
    getmaxyx(stdscr, max_y, max_x);
    max_y -= 1;
    m_dimensions = { max_y, max_x };
}

void NCursesUI::draw(const DisplayBuffer& display_buffer,
                     const String& status_line)
{
    LineCount line_index = 0;
    for (const DisplayLine& line : display_buffer.lines())
    {
        move((int)line_index, 0);
        clrtoeol();
        for (const DisplayAtom& atom : line)
        {
            set_attribute(A_UNDERLINE, atom.attribute & Underline);
            set_attribute(A_REVERSE, atom.attribute & Reverse);
            set_attribute(A_BLINK, atom.attribute & Blink);
            set_attribute(A_BOLD, atom.attribute & Bold);

            set_color(atom.fg_color, atom.bg_color);

            String content = atom.content.content();
            int y,x;
            getyx(stdscr, y,x);
            if (content[content.length()-1] == '\n' and
                content.char_length() - 1 < m_dimensions.column - x)
            {
                addutf8str(Utf8Iterator(content.begin()), Utf8Iterator(content.end())-1);
                addch(' ');
            }
            else
            {
                Utf8Iterator begin(content.begin()), end(content.end());
                if (end - begin > m_dimensions.column - x)
                    end = begin + m_dimensions.column - x;
                addutf8str(begin, end);
            }
        }
        ++line_index;
    }

    set_attribute(A_UNDERLINE, 0);
    set_attribute(A_REVERSE, 0);
    set_attribute(A_BLINK, 0);
    set_attribute(A_BOLD, 0);
    set_color(Color::Blue, Color::Black);
    for (;line_index < m_dimensions.line; ++line_index)
    {
        move((int)line_index, 0);
        clrtoeol();
        addch('~');
    }

    set_color(Color::Cyan, Color::Black);
    static int last_status_length = 0;
    move((int)m_dimensions.line, (int)(m_dimensions.column - last_status_length));
    clrtoeol();
    move((int)m_dimensions.line, (int)(m_dimensions.column - status_line.char_length()));
    addstr(status_line.c_str());
    last_status_length = (int)status_line.length();

    redraw(m_menu_win);
}

struct getch_iterator
{
    int operator*() { return getch(); }
    getch_iterator& operator++() { return *this; }
    getch_iterator& operator++(int) { return *this; }
};

Key NCursesUI::get_key()
{
    const unsigned c = getch();
    if (c > 0 and c < 27)
    {
        return {Key::Modifiers::Control, c - 1 + 'a'};
    }
    else if (c == 27)
    {
        timeout(0);
        const Codepoint new_c = getch();
        timeout(-1);
        if (new_c != ERR)
            return {Key::Modifiers::Alt, new_c};
        else
            return Key::Escape;
    }
    else switch (c)
    {
    case KEY_BACKSPACE: return Key::Backspace;
    case KEY_UP: return Key::Up;
    case KEY_DOWN: return Key::Down;
    case KEY_LEFT: return Key::Left;
    case KEY_RIGHT: return Key::Right;
    case KEY_PPAGE: return Key::PageUp;
    case KEY_NPAGE: return Key::PageDown;
    case KEY_BTAB: return Key::BackTab;
    }

    if (c < 256)
    {
       ungetch(c);
       return utf8::codepoint(getch_iterator{});
    }
    return Key::Invalid;
}

void NCursesUI::print_status(const String& status, CharCount cursor_pos)
{
    int x,y;
    getmaxyx(stdscr, y, x);
    move(y-1, 0);
    clrtoeol();
    if (cursor_pos == -1)
       addutf8str(status.begin(), status.end());
    else
    {
        auto cursor_it = utf8::advance(status.begin(), status.end(), (int)cursor_pos);
        auto end = status.end();
        addutf8str(status.begin(), cursor_it);
        set_attribute(A_REVERSE, 1);
        addch((cursor_it == end) ? ' ' : utf8::codepoint<Utf8Policy>(cursor_it));
        set_attribute(A_REVERSE, 0);
        if (cursor_it != end)
            addutf8str(utf8::next(cursor_it), end);
    }
    redraw(m_menu_win);
}

void NCursesUI::menu_show(const memoryview<String>& choices,
                          const DisplayCoord& anchor, MenuStyle style)
{
    assert(m_menu == nullptr);
    assert(m_menu_win == nullptr);
    m_choices = std::vector<String>(choices.begin(), choices.end());
    CharCount longest = 0;
    for (int i = 0; i < m_choices.size(); ++i)
    {
        m_items.push_back(new_item(m_choices[i].c_str(), ""));
        longest = std::max(longest, m_choices[i].char_length());
    }
    m_items.push_back(nullptr);
    longest += 1;

    int max_x,max_y;
    getmaxyx(stdscr, max_y, max_x);
    max_x -= (int)anchor.column;

    int columns = (style == MenuStyle::Prompt) ?
                  (max_x / std::min(max_x, (int)longest)) : 1;
    int lines = std::min(10, (int)ceilf((float)m_choices.size()/columns));

    m_menu_pos = { anchor.line+1, anchor.column };
    if (m_menu_pos.line + lines >= max_y)
        m_menu_pos.line = anchor.line - lines;
    m_menu_size = { lines, columns == 1 ? longest : max_x };

    m_menu = new_menu(&m_items[0]);
    m_menu_win = newwin((int)m_menu_size.line, (int)m_menu_size.column,
                        (int)m_menu_pos.line,  (int)m_menu_pos.column);
    set_menu_win(m_menu, m_menu_win);
    set_menu_format(m_menu, lines, columns);
    set_menu_mark(m_menu, nullptr);
    set_menu_fore(m_menu, COLOR_PAIR(m_menu_fg));
    set_menu_back(m_menu, COLOR_PAIR(m_menu_bg));
    post_menu(m_menu);
}

void NCursesUI::menu_select(int selected)
{
    // last item in m_items is the nullptr, hence the - 1
    if (selected >= 0 and selected < m_items.size() - 1)
    {
        set_menu_fore(m_menu, COLOR_PAIR(m_menu_fg));
        set_current_item(m_menu, m_items[selected]);
    }
    else
        set_menu_fore(m_menu, COLOR_PAIR(m_menu_bg));
}

void NCursesUI::menu_hide()
{
    if (not m_menu)
        return;
    unpost_menu(m_menu);
    free_menu(m_menu);
    for (auto item : m_items)
       if (item)
           free_item(item);
    m_menu = nullptr;
    delwin(m_menu_win);
    m_menu_win = nullptr;
    m_items.clear();
}

DisplayCoord NCursesUI::dimensions()
{
    return m_dimensions;
}

}
<commit_msg>NCursesUI: force input handling reexecution when resizing<commit_after>#include "ncurses.hh"

#include "display_buffer.hh"
#include "register_manager.hh"

#include "utf8_iterator.hh"
#include "event_manager.hh"

#include <map>

#include <signal.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <fcntl.h>

namespace Kakoune
{

static void set_attribute(int attribute, bool on)
{
    if (on)
        attron(attribute);
    else
        attroff(attribute);
}

static int nc_color(Color color)
{
    switch (color)
    {
    case Color::Black:   return COLOR_BLACK;
    case Color::Red:     return COLOR_RED;
    case Color::Green:   return COLOR_GREEN;
    case Color::Yellow:  return COLOR_YELLOW;
    case Color::Blue:    return COLOR_BLUE;
    case Color::Magenta: return COLOR_MAGENTA;
    case Color::Cyan:    return COLOR_CYAN;
    case Color::White:   return COLOR_WHITE;

    case Color::Default:
    default:
        return -1;
    }
}

static int get_color_pair(Color fg_color, Color bg_color)
{
    static std::map<std::pair<Color, Color>, int> colorpairs;
    static int next_pair = 1;

    std::pair<Color, Color> colorpair(fg_color, bg_color);

    auto it = colorpairs.find(colorpair);
    if (it != colorpairs.end())
        return it->second;
    else
    {
        init_pair(next_pair, nc_color(fg_color), nc_color(bg_color));
        colorpairs[colorpair] = next_pair;
        return next_pair++;
    }
}

static void set_color(Color fg_color, Color bg_color)
{
    static int current_pair = -1;

    if (current_pair != -1)
        attroff(COLOR_PAIR(current_pair));

    if (fg_color != Color::Default or bg_color != Color::Default)
    {
        current_pair = get_color_pair(fg_color, bg_color);
        attron(COLOR_PAIR(current_pair));
    }
}

static NCursesUI* signal_ui = nullptr;
void on_term_resize(int)
{
    int fd = open("/dev/tty", O_RDWR);
    winsize ws;
    if (fd == 0 or ioctl(fd, TIOCGWINSZ, (void*)&ws) != 0)
        return;
    close(fd);
    resizeterm(ws.ws_row, ws.ws_col);
    ungetch(KEY_RESIZE);
    signal_ui->update_dimensions();
    EventManager::instance().force_signal(0);
}

NCursesUI::NCursesUI()
{
    //setlocale(LC_CTYPE, "");
    initscr();
    cbreak();
    noecho();
    nonl();
    intrflush(stdscr, false);
    keypad(stdscr, true);
    curs_set(0);
    start_color();
    use_default_colors();
    ESCDELAY=25;

    m_menu_fg = get_color_pair(Color::Blue, Color::Cyan);
    m_menu_bg = get_color_pair(Color::Cyan, Color::Blue);

    update_dimensions();

    assert(signal_ui == nullptr);
    signal_ui = this;
    signal(SIGWINCH, on_term_resize);
}

NCursesUI::~NCursesUI()
{
    endwin();
}

static void redraw(WINDOW* menu_win)
{
    wnoutrefresh(stdscr);
    if (menu_win)
    {
        redrawwin(menu_win);
        wnoutrefresh(menu_win);
    }
    doupdate();
}
using Utf8Policy = utf8::InvalidBytePolicy::Pass;
using Utf8Iterator = utf8::utf8_iterator<String::iterator, Utf8Policy>;
void addutf8str(Utf8Iterator begin, Utf8Iterator end)
{
    while (begin != end)
        addch(*begin++);
}

void NCursesUI::update_dimensions()
{
    int max_x,max_y;
    getmaxyx(stdscr, max_y, max_x);
    max_y -= 1;
    m_dimensions = { max_y, max_x };
}

void NCursesUI::draw(const DisplayBuffer& display_buffer,
                     const String& status_line)
{
    LineCount line_index = 0;
    for (const DisplayLine& line : display_buffer.lines())
    {
        move((int)line_index, 0);
        clrtoeol();
        for (const DisplayAtom& atom : line)
        {
            set_attribute(A_UNDERLINE, atom.attribute & Underline);
            set_attribute(A_REVERSE, atom.attribute & Reverse);
            set_attribute(A_BLINK, atom.attribute & Blink);
            set_attribute(A_BOLD, atom.attribute & Bold);

            set_color(atom.fg_color, atom.bg_color);

            String content = atom.content.content();
            int y,x;
            getyx(stdscr, y,x);
            if (content[content.length()-1] == '\n' and
                content.char_length() - 1 < m_dimensions.column - x)
            {
                addutf8str(Utf8Iterator(content.begin()), Utf8Iterator(content.end())-1);
                addch(' ');
            }
            else
            {
                Utf8Iterator begin(content.begin()), end(content.end());
                if (end - begin > m_dimensions.column - x)
                    end = begin + m_dimensions.column - x;
                addutf8str(begin, end);
            }
        }
        ++line_index;
    }

    set_attribute(A_UNDERLINE, 0);
    set_attribute(A_REVERSE, 0);
    set_attribute(A_BLINK, 0);
    set_attribute(A_BOLD, 0);
    set_color(Color::Blue, Color::Black);
    for (;line_index < m_dimensions.line; ++line_index)
    {
        move((int)line_index, 0);
        clrtoeol();
        addch('~');
    }

    set_color(Color::Cyan, Color::Black);
    static int last_status_length = 0;
    move((int)m_dimensions.line, (int)(m_dimensions.column - last_status_length));
    clrtoeol();
    move((int)m_dimensions.line, (int)(m_dimensions.column - status_line.char_length()));
    addstr(status_line.c_str());
    last_status_length = (int)status_line.length();

    redraw(m_menu_win);
}

struct getch_iterator
{
    int operator*() { return getch(); }
    getch_iterator& operator++() { return *this; }
    getch_iterator& operator++(int) { return *this; }
};

Key NCursesUI::get_key()
{
    const unsigned c = getch();
    if (c > 0 and c < 27)
    {
        return {Key::Modifiers::Control, c - 1 + 'a'};
    }
    else if (c == 27)
    {
        timeout(0);
        const Codepoint new_c = getch();
        timeout(-1);
        if (new_c != ERR)
            return {Key::Modifiers::Alt, new_c};
        else
            return Key::Escape;
    }
    else switch (c)
    {
    case KEY_BACKSPACE: return Key::Backspace;
    case KEY_UP: return Key::Up;
    case KEY_DOWN: return Key::Down;
    case KEY_LEFT: return Key::Left;
    case KEY_RIGHT: return Key::Right;
    case KEY_PPAGE: return Key::PageUp;
    case KEY_NPAGE: return Key::PageDown;
    case KEY_BTAB: return Key::BackTab;
    }

    if (c < 256)
    {
       ungetch(c);
       return utf8::codepoint(getch_iterator{});
    }
    return Key::Invalid;
}

void NCursesUI::print_status(const String& status, CharCount cursor_pos)
{
    int x,y;
    getmaxyx(stdscr, y, x);
    move(y-1, 0);
    clrtoeol();
    if (cursor_pos == -1)
       addutf8str(status.begin(), status.end());
    else
    {
        auto cursor_it = utf8::advance(status.begin(), status.end(), (int)cursor_pos);
        auto end = status.end();
        addutf8str(status.begin(), cursor_it);
        set_attribute(A_REVERSE, 1);
        addch((cursor_it == end) ? ' ' : utf8::codepoint<Utf8Policy>(cursor_it));
        set_attribute(A_REVERSE, 0);
        if (cursor_it != end)
            addutf8str(utf8::next(cursor_it), end);
    }
    redraw(m_menu_win);
}

void NCursesUI::menu_show(const memoryview<String>& choices,
                          const DisplayCoord& anchor, MenuStyle style)
{
    assert(m_menu == nullptr);
    assert(m_menu_win == nullptr);
    m_choices = std::vector<String>(choices.begin(), choices.end());
    CharCount longest = 0;
    for (int i = 0; i < m_choices.size(); ++i)
    {
        m_items.push_back(new_item(m_choices[i].c_str(), ""));
        longest = std::max(longest, m_choices[i].char_length());
    }
    m_items.push_back(nullptr);
    longest += 1;

    int max_x,max_y;
    getmaxyx(stdscr, max_y, max_x);
    max_x -= (int)anchor.column;

    int columns = (style == MenuStyle::Prompt) ?
                  (max_x / std::min(max_x, (int)longest)) : 1;
    int lines = std::min(10, (int)ceilf((float)m_choices.size()/columns));

    m_menu_pos = { anchor.line+1, anchor.column };
    if (m_menu_pos.line + lines >= max_y)
        m_menu_pos.line = anchor.line - lines;
    m_menu_size = { lines, columns == 1 ? longest : max_x };

    m_menu = new_menu(&m_items[0]);
    m_menu_win = newwin((int)m_menu_size.line, (int)m_menu_size.column,
                        (int)m_menu_pos.line,  (int)m_menu_pos.column);
    set_menu_win(m_menu, m_menu_win);
    set_menu_format(m_menu, lines, columns);
    set_menu_mark(m_menu, nullptr);
    set_menu_fore(m_menu, COLOR_PAIR(m_menu_fg));
    set_menu_back(m_menu, COLOR_PAIR(m_menu_bg));
    post_menu(m_menu);
}

void NCursesUI::menu_select(int selected)
{
    // last item in m_items is the nullptr, hence the - 1
    if (selected >= 0 and selected < m_items.size() - 1)
    {
        set_menu_fore(m_menu, COLOR_PAIR(m_menu_fg));
        set_current_item(m_menu, m_items[selected]);
    }
    else
        set_menu_fore(m_menu, COLOR_PAIR(m_menu_bg));
}

void NCursesUI::menu_hide()
{
    if (not m_menu)
        return;
    unpost_menu(m_menu);
    free_menu(m_menu);
    for (auto item : m_items)
       if (item)
           free_item(item);
    m_menu = nullptr;
    delwin(m_menu_win);
    m_menu_win = nullptr;
    m_items.clear();
}

DisplayCoord NCursesUI::dimensions()
{
    return m_dimensions;
}

}
<|endoftext|>


<commit_before>#include <iostream>
#include <fstream>
#include <chrono>

#include "loader.h"
#include "hdd.h"
#include "cpu.h"
#include "ram.h"

using namespace std;

int main(int argc, char const *argv[])
{
    HDD *hdd = new HDD();
#ifdef __APPLE__
	Loader *l = new Loader("./spec/Program-File.txt", hdd);
#elif _WIN32
	Loader *l = new Loader("..\\OSProj\\spec\\Program-File.txt", hdd);
#endif

    CPU *cpu = new CPU();

    File * file = hdd->findFile(1);
    WORD * programData = (WORD *)&(*(file+1));

	std::chrono::steady_clock::time_point start;
	std::chrono::steady_clock::time_point end;
    for (unsigned i = 0; i < file->programSize; i++)
    {
		// start = std::chrono::steady_clock::now();
        cpu->fetch(programData++);
        cpu->decode();
		// end = std::chrono::steady_clock::now();
		// std::cout << "Execution Time " << std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count()<<'\n';
    }

    RAM *ram = new RAM();

	return 0;
}
<commit_msg>accidentally commited a local comment<commit_after>#include <iostream>
#include <fstream>
#include <chrono>

#include "loader.h"
#include "hdd.h"
#include "cpu.h"
#include "ram.h"

using namespace std;

int main(int argc, char const *argv[])
{
    HDD *hdd = new HDD();
#ifdef __APPLE__
	Loader *l = new Loader("./spec/Program-File.txt", hdd);
#elif _WIN32
	Loader *l = new Loader("..\\OSProj\\spec\\Program-File.txt", hdd);
#endif

    CPU *cpu = new CPU();

    File * file = hdd->findFile(1);
    WORD * programData = (WORD *)&(*(file+1));

	std::chrono::steady_clock::time_point start;
	std::chrono::steady_clock::time_point end;
    for (unsigned i = 0; i < file->programSize; i++)
    {
		start = std::chrono::steady_clock::now();
        cpu->fetch(programData++);
        cpu->decode();
		end = std::chrono::steady_clock::now();
		std::cout << "Execution Time " << std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count()<<'\n';
    }

    RAM *ram = new RAM();

	return 0;
}
<|endoftext|>


<commit_before>#include <iostream>

#include "ast/attachpoint_parser.h"
#include "driver.h"
#include "log.h"

extern void *yy_scan_string(const char *yy_str, yyscan_t yyscanner);
extern int yylex_init(yyscan_t *scanner);
extern int yylex_destroy (yyscan_t yyscanner);
extern bpftrace::location loc;

namespace bpftrace {

Driver::Driver(BPFtrace &bpftrace, std::ostream &o) : bpftrace_(bpftrace), out_(o)
{
}

Driver::~Driver()
{
  delete root_;
}

void Driver::source(std::string filename, std::string script)
{
  Log::get().set_source(filename, script);
}

// Kept for the test suite
int Driver::parse_str(std::string script)
{
  source("stdin", script);
  return parse();
}

int Driver::parse()
{
  // Ensure we free memory allocated the previous parse if we parse
  // more than once
  delete root_;
  root_ = nullptr;

  // Reset source location info on every pass
  loc.initialize();

  yyscan_t scanner;
  yylex_init(&scanner);
  Parser parser(*this, scanner);
  yy_scan_string(Log::get().get_source().c_str(), scanner);
  parser.parse();
  yylex_destroy(scanner);

  ast::AttachPointParser ap_parser(root_, bpftrace_, out_);
  if (ap_parser.parse())
    failed_ = true;

  // Keep track of errors thrown ourselves, since the result of
  // parser_->parse() doesn't take scanner errors into account,
  // only parser errors.
  return failed_;
}

void Driver::error(const location &l, const std::string &m)
{
  LOG(ERROR, l, out_) << m;
  failed_ = true;
}

void Driver::error(const std::string &m)
{
  LOG(ERROR, out_) << m;
  failed_ = true;
}

} // namespace bpftrace
<commit_msg>Delete driver.root_ when failing parse<commit_after>#include <iostream>

#include "ast/attachpoint_parser.h"
#include "driver.h"
#include "log.h"

extern void *yy_scan_string(const char *yy_str, yyscan_t yyscanner);
extern int yylex_init(yyscan_t *scanner);
extern int yylex_destroy (yyscan_t yyscanner);
extern bpftrace::location loc;

namespace bpftrace {

Driver::Driver(BPFtrace &bpftrace, std::ostream &o) : bpftrace_(bpftrace), out_(o)
{
}

Driver::~Driver()
{
  delete root_;
}

void Driver::source(std::string filename, std::string script)
{
  Log::get().set_source(filename, script);
}

// Kept for the test suite
int Driver::parse_str(std::string script)
{
  source("stdin", script);
  return parse();
}

int Driver::parse()
{
  // Ensure we free memory allocated the previous parse if we parse
  // more than once
  delete root_;
  root_ = nullptr;

  // Reset source location info on every pass
  loc.initialize();

  yyscan_t scanner;
  yylex_init(&scanner);
  Parser parser(*this, scanner);
  yy_scan_string(Log::get().get_source().c_str(), scanner);
  parser.parse();
  yylex_destroy(scanner);

  if (!failed_)
  {
    ast::AttachPointParser ap_parser(root_, bpftrace_, out_);
    if (ap_parser.parse())
      failed_ = true;
  }

  if (failed_)
  {
    delete root_;
    root_ = nullptr;
  }

  // Keep track of errors thrown ourselves, since the result of
  // parser_->parse() doesn't take scanner errors into account,
  // only parser errors.
  return failed_;
}

void Driver::error(const location &l, const std::string &m)
{
  LOG(ERROR, l, out_) << m;
  failed_ = true;
}

void Driver::error(const std::string &m)
{
  LOG(ERROR, out_) << m;
  failed_ = true;
}

} // namespace bpftrace
<|endoftext|>


<commit_before>/*************************************************************************
 *   Copyright (C) 2011-2014 by Paul-Louis Ageneau                       *
 *   paul-louis (at) ageneau (dot) org                                   *
 *                                                                       *
 *   This file is part of Teapotnet.                                     *
 *                                                                       *
 *   Teapotnet is free software: you can redistribute it and/or modify   *
 *   it under the terms of the GNU Affero General Public License as      *
 *   published by the Free Software Foundation, either version 3 of      *
 *   the License, or (at your option) any later version.                 *
 *                                                                       *
 *   Teapotnet is distributed in the hope that it will be useful, but    *
 *   WITHOUT ANY WARRANTY; without even the implied warranty of          *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the        *
 *   GNU Affero General Public License for more details.                 *
 *                                                                       *
 *   You should have received a copy of the GNU Affero General Public    *
 *   License along with Teapotnet.                                       *
 *   If not, see <http://www.gnu.org/licenses/>.                         *
 *************************************************************************/

#include "tpn/store.h"
#include "tpn/config.h"
#include "tpn/network.h"
#include "tpn/cache.h"

#include "pla/directory.h"
#include "pla/crypto.h"

namespace tpn
{

Store *Store::Instance = NULL;

BinaryString Store::Hash(const String &str)
{
	return Sha256().compute(str);
}

Store::Store(void)
{
	mDatabase = new Database("store.db");

	mDatabase->execute("CREATE TABLE IF NOT EXISTS blocks\
		(id INTEGER PRIMARY KEY AUTOINCREMENT,\
		digest BLOB,\
		file_id INTEGER,\
		offset INTEGER(8),\
		size INTEGER(8))");
	mDatabase->execute("CREATE INDEX IF NOT EXISTS digest ON blocks (digest)");
	mDatabase->execute("CREATE UNIQUE INDEX IF NOT EXISTS location ON blocks (file_id, offset)");

	mDatabase->execute("CREATE TABLE IF NOT EXISTS files\
		(id INTEGER PRIMARY KEY AUTOINCREMENT,\
		name TEXT UNIQUE)");
	mDatabase->execute("CREATE INDEX IF NOT EXISTS name ON files (name)");

	mDatabase->execute("CREATE TABLE IF NOT EXISTS map\
		(key BLOB,\
		value BLOB,\
		time INTEGER(8),\
		type INTEGER(1))");
	mDatabase->execute("CREATE UNIQUE INDEX IF NOT EXISTS pair ON map (key, value)");

	// Store is scheduled by Overlay on first connection
	Scheduler::Global->repeat(this, 600.);
}

Store::~Store(void)
{

}

bool Store::push(const BinaryString &digest, Fountain::Combination &input)
{
	Synchronize(this);

	if(hasBlock(digest)) return true;

	Fountain::Sink &sink = mSinks[digest];
	sink.solve(input);
	if(!sink.isDecoded()) return false;

	BinaryString sinkDigest;
	sink.hash(sinkDigest);

	LogDebug("Store::push", "Block is complete, digest is " + sinkDigest.toString());

	if(sinkDigest != digest)
	{
		LogWarn("Store::push", "Block digest is invalid (expected " + digest.toString() + ")");
		mSinks.erase(digest);
		return false;
	}

	String path = Cache::Instance->path(digest);

	File file(path, File::Write);
	int64_t size = sink.dump(file);
	file.close();

	notifyBlock(digest, path, 0, size);
	mSinks.erase(digest);
	return true;
}

bool Store::pull(const BinaryString &digest, Fountain::Combination &output, unsigned *tokens)
{
	Synchronize(this);

	int64_t size;
	File *file = getBlock(digest, size);
	if(!file) return false;

	Fountain::FileSource source(file, file->tellRead(), size);
	source.generate(output, tokens);
	return true;
}

bool Store::hasBlock(const BinaryString &digest)
{
	Synchronize(this);

	Database::Statement statement = mDatabase->prepare("SELECT 1 FROM blocks WHERE digest = ?1");
	statement.bind(1, digest);
	if(statement.step())
	{
		statement.finalize();
		return true;
	}

	statement.finalize();
	return false;
}

void Store::waitBlock(const BinaryString &digest)
{
	if(!waitBlock(digest, 60.))	// TODO
		throw Timeout();
}

bool Store::waitBlock(const BinaryString &digest, double &timeout)
{
	Synchronize(this);

	if(!hasBlock(digest))
	{
		Desynchronize(this);
		Network::Caller caller(digest);		// Block is missing locally, call it

		LogDebug("Store::waitBlock", "Waiting for block: " + digest.toString());

		{
			Synchronize(this);

			while(!hasBlock(digest))
				if(!wait(timeout))
					return false;
		}

		LogDebug("Store::waitBlock", "Block is now available: " + digest.toString());
	}

	return true;
}

bool Store::waitBlock(const BinaryString &digest, const double &timeout)
{
	double dummy = timeout;
	return waitBlock(digest, dummy);
}

File *Store::getBlock(const BinaryString &digest, int64_t &size)
{
	Synchronize(this);

	Database::Statement statement = mDatabase->prepare("SELECT f.name, b.offset, b.size FROM blocks b LEFT JOIN files f ON f.id = b.file_id WHERE b.digest = ?1 LIMIT 1");
	statement.bind(1, digest);
	if(statement.step())
	{
		String filename;
		int64_t offset;
		statement.value(0, filename);
		statement.value(1, offset);
		statement.value(2, size);
		statement.finalize();

		try {
			File *file = new File(filename);
			file->seekRead(offset);
			return file;
		}
		catch(...)
		{
			notifyFileErasure(filename);
		}

		return NULL;
	}

	statement.finalize();
	return NULL;
}

void Store::notifyBlock(const BinaryString &digest, const String &filename, int64_t offset, int64_t size)
{
	Synchronize(this);

	//LogDebug("Store::notifyBlock", "Block notified: " + digest.toString());

	Database::Statement statement = mDatabase->prepare("INSERT OR IGNORE INTO files (name) VALUES (?1)");
	statement.bind(1, filename);
	statement.execute();

	statement = mDatabase->prepare("INSERT OR REPLACE INTO blocks (file_id, digest, offset, size) VALUES ((SELECT id FROM files WHERE name = ?1 LIMIT 1), ?2, ?3, ?4)");
	statement.bind(1, filename);
	statement.bind(2, digest);
	statement.bind(3, offset);
	statement.bind(4, size);
	statement.execute();

	notifyAll();

	// Publish into DHT
	DesynchronizeStatement(this, Network::Instance->storeValue(digest, Network::Instance->overlay()->localNode()));
}

void Store::notifyFileErasure(const String &filename)
{
	Synchronize(this);

	Database::Statement statement = mDatabase->prepare("DELETE FROM blocks WHERE file_id = (SELECT id FROM files WHERE name = ?1)");
	statement.bind(1, filename);
	statement.execute();

	statement = mDatabase->prepare("DELETE FROM files WHERE name = ?1");
	statement.bind(1, filename);
	statement.execute();
}

void Store::storeValue(const BinaryString &key, const BinaryString &value, Store::ValueType type)
{
	Synchronize(this);

	if(type == Permanent)
	{
		Database::Statement statement = mDatabase->prepare("DELETE FROM map WHERE key = ?1 AND type = ?2");
		statement.bind(1, key);
		statement.bind(2, static_cast<int>(Permanent));
		statement.execute();
	}

	Database::Statement statement = mDatabase->prepare("INSERT OR REPLACE INTO map (key, value, time, type) VALUES (?1, ?2, ?3, ?4)");
	statement.bind(1, key);
	statement.bind(2, value);
	statement.bind(3, Time::Now());
	statement.bind(4, static_cast<int>(type));
	statement.execute();
}

bool Store::retrieveValue(const BinaryString &key, Set<BinaryString> &values)
{
	Synchronize(this);

	Database::Statement statement = mDatabase->prepare("SELECT value FROM map WHERE key = ?1");
	statement.bind(1, key);
	while(statement.step())
	{
		BinaryString v;
		statement.value(0, v);
		values.insert(v);
	}

	statement.finalize();
	return !values.empty();
}

void Store::run(void)
{
	Synchronize(this);

	const double maxAge = 2*3600.;	// TODO
	const double delay = 0.1;	// TODO
	const int batch = 10;		// TODO

	BinaryString node;
	DesynchronizeStatement(this, node = Network::Instance->overlay()->localNode());

	LogDebug("Store::run", "Started");

	// Delete old non-permanent values
	Database::Statement statement = mDatabase->prepare("DELETE FROM map WHERE type != ?1 AND time < ?2");
	statement.bind(1, static_cast<int>(Permanent));
	statement.bind(2, Time::Now() - maxAge);
	statement.execute();

	try {
		// Publish everything into DHT periodically
		int offset = 0;
		while(true)
		{
			int n;
			DesynchronizeStatement(this, n = Network::Instance->overlay()->connectionsCount());
			if(n == 0)
			{
				LogDebug("Store::run", "Interrupted");
				return;
			}

			// Select DHT values
			Database::Statement statement = mDatabase->prepare("SELECT digest FROM blocks WHERE type = ?1 AND digest IS NOT NULL ORDER BY digest LIMIT ?2 OFFSET ?3");
			statement.bind(1, static_cast<int>(Distributed));
			statement.bind(2, batch);
			statement.bind(3, offset);

			List<BinaryString> result;
			statement.fetchColumn(0, result);
			statement.finalize();

			if(result.empty()) break;
			else {
				Desynchronize(this);

				for(List<BinaryString>::iterator it = result.begin(); it != result.end(); ++it)
					Network::Instance->storeValue(*it, node);
			}

			offset+= result.size();
			Thread::Sleep(delay);
		}

		LogDebug("Store::run", "Finished, " + String::number(offset) + " values published");
	}
	catch(const std::exception &e)
	{
		LogWarn("Store::run", e.what());
	}
}

}
<commit_msg>Fixed Store::run()<commit_after>/*************************************************************************
 *   Copyright (C) 2011-2014 by Paul-Louis Ageneau                       *
 *   paul-louis (at) ageneau (dot) org                                   *
 *                                                                       *
 *   This file is part of Teapotnet.                                     *
 *                                                                       *
 *   Teapotnet is free software: you can redistribute it and/or modify   *
 *   it under the terms of the GNU Affero General Public License as      *
 *   published by the Free Software Foundation, either version 3 of      *
 *   the License, or (at your option) any later version.                 *
 *                                                                       *
 *   Teapotnet is distributed in the hope that it will be useful, but    *
 *   WITHOUT ANY WARRANTY; without even the implied warranty of          *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the        *
 *   GNU Affero General Public License for more details.                 *
 *                                                                       *
 *   You should have received a copy of the GNU Affero General Public    *
 *   License along with Teapotnet.                                       *
 *   If not, see <http://www.gnu.org/licenses/>.                         *
 *************************************************************************/

#include "tpn/store.h"
#include "tpn/config.h"
#include "tpn/network.h"
#include "tpn/cache.h"

#include "pla/directory.h"
#include "pla/crypto.h"

namespace tpn
{

Store *Store::Instance = NULL;

BinaryString Store::Hash(const String &str)
{
	return Sha256().compute(str);
}

Store::Store(void)
{
	mDatabase = new Database("store.db");

	mDatabase->execute("CREATE TABLE IF NOT EXISTS blocks\
		(id INTEGER PRIMARY KEY AUTOINCREMENT,\
		digest BLOB,\
		file_id INTEGER,\
		offset INTEGER(8),\
		size INTEGER(8))");
	mDatabase->execute("CREATE INDEX IF NOT EXISTS digest ON blocks (digest)");
	mDatabase->execute("CREATE UNIQUE INDEX IF NOT EXISTS location ON blocks (file_id, offset)");

	mDatabase->execute("CREATE TABLE IF NOT EXISTS files\
		(id INTEGER PRIMARY KEY AUTOINCREMENT,\
		name TEXT UNIQUE)");
	mDatabase->execute("CREATE INDEX IF NOT EXISTS name ON files (name)");

	mDatabase->execute("CREATE TABLE IF NOT EXISTS map\
		(key BLOB,\
		value BLOB,\
		time INTEGER(8),\
		type INTEGER(1))");
	mDatabase->execute("CREATE UNIQUE INDEX IF NOT EXISTS pair ON map (key, value)");

	// Store is scheduled by Overlay on first connection
	Scheduler::Global->repeat(this, 600.);
}

Store::~Store(void)
{

}

bool Store::push(const BinaryString &digest, Fountain::Combination &input)
{
	Synchronize(this);

	if(hasBlock(digest)) return true;

	Fountain::Sink &sink = mSinks[digest];
	sink.solve(input);
	if(!sink.isDecoded()) return false;

	BinaryString sinkDigest;
	sink.hash(sinkDigest);

	LogDebug("Store::push", "Block is complete, digest is " + sinkDigest.toString());

	if(sinkDigest != digest)
	{
		LogWarn("Store::push", "Block digest is invalid (expected " + digest.toString() + ")");
		mSinks.erase(digest);
		return false;
	}

	String path = Cache::Instance->path(digest);

	File file(path, File::Write);
	int64_t size = sink.dump(file);
	file.close();

	notifyBlock(digest, path, 0, size);
	mSinks.erase(digest);
	return true;
}

bool Store::pull(const BinaryString &digest, Fountain::Combination &output, unsigned *tokens)
{
	Synchronize(this);

	int64_t size;
	File *file = getBlock(digest, size);
	if(!file) return false;

	Fountain::FileSource source(file, file->tellRead(), size);
	source.generate(output, tokens);
	return true;
}

bool Store::hasBlock(const BinaryString &digest)
{
	Synchronize(this);

	Database::Statement statement = mDatabase->prepare("SELECT 1 FROM blocks WHERE digest = ?1");
	statement.bind(1, digest);
	if(statement.step())
	{
		statement.finalize();
		return true;
	}

	statement.finalize();
	return false;
}

void Store::waitBlock(const BinaryString &digest)
{
	if(!waitBlock(digest, 60.))	// TODO
		throw Timeout();
}

bool Store::waitBlock(const BinaryString &digest, double &timeout)
{
	Synchronize(this);

	if(!hasBlock(digest))
	{
		Desynchronize(this);
		Network::Caller caller(digest);		// Block is missing locally, call it

		LogDebug("Store::waitBlock", "Waiting for block: " + digest.toString());

		{
			Synchronize(this);

			while(!hasBlock(digest))
				if(!wait(timeout))
					return false;
		}

		LogDebug("Store::waitBlock", "Block is now available: " + digest.toString());
	}

	return true;
}

bool Store::waitBlock(const BinaryString &digest, const double &timeout)
{
	double dummy = timeout;
	return waitBlock(digest, dummy);
}

File *Store::getBlock(const BinaryString &digest, int64_t &size)
{
	Synchronize(this);

	Database::Statement statement = mDatabase->prepare("SELECT f.name, b.offset, b.size FROM blocks b LEFT JOIN files f ON f.id = b.file_id WHERE b.digest = ?1 LIMIT 1");
	statement.bind(1, digest);
	if(statement.step())
	{
		String filename;
		int64_t offset;
		statement.value(0, filename);
		statement.value(1, offset);
		statement.value(2, size);
		statement.finalize();

		try {
			File *file = new File(filename);
			file->seekRead(offset);
			return file;
		}
		catch(...)
		{
			notifyFileErasure(filename);
		}

		return NULL;
	}

	statement.finalize();
	return NULL;
}

void Store::notifyBlock(const BinaryString &digest, const String &filename, int64_t offset, int64_t size)
{
	Synchronize(this);

	//LogDebug("Store::notifyBlock", "Block notified: " + digest.toString());

	Database::Statement statement = mDatabase->prepare("INSERT OR IGNORE INTO files (name) VALUES (?1)");
	statement.bind(1, filename);
	statement.execute();

	statement = mDatabase->prepare("INSERT OR REPLACE INTO blocks (file_id, digest, offset, size) VALUES ((SELECT id FROM files WHERE name = ?1 LIMIT 1), ?2, ?3, ?4)");
	statement.bind(1, filename);
	statement.bind(2, digest);
	statement.bind(3, offset);
	statement.bind(4, size);
	statement.execute();

	notifyAll();

	// Publish into DHT
	DesynchronizeStatement(this, Network::Instance->storeValue(digest, Network::Instance->overlay()->localNode()));
}

void Store::notifyFileErasure(const String &filename)
{
	Synchronize(this);

	Database::Statement statement = mDatabase->prepare("DELETE FROM blocks WHERE file_id = (SELECT id FROM files WHERE name = ?1)");
	statement.bind(1, filename);
	statement.execute();

	statement = mDatabase->prepare("DELETE FROM files WHERE name = ?1");
	statement.bind(1, filename);
	statement.execute();
}

void Store::storeValue(const BinaryString &key, const BinaryString &value, Store::ValueType type)
{
	Synchronize(this);

	if(type == Permanent)
	{
		Database::Statement statement = mDatabase->prepare("DELETE FROM map WHERE key = ?1 AND type = ?2");
		statement.bind(1, key);
		statement.bind(2, static_cast<int>(Permanent));
		statement.execute();
	}

	Database::Statement statement = mDatabase->prepare("INSERT OR REPLACE INTO map (key, value, time, type) VALUES (?1, ?2, ?3, ?4)");
	statement.bind(1, key);
	statement.bind(2, value);
	statement.bind(3, Time::Now());
	statement.bind(4, static_cast<int>(type));
	statement.execute();
}

bool Store::retrieveValue(const BinaryString &key, Set<BinaryString> &values)
{
	Synchronize(this);

	Database::Statement statement = mDatabase->prepare("SELECT value FROM map WHERE key = ?1");
	statement.bind(1, key);
	while(statement.step())
	{
		BinaryString v;
		statement.value(0, v);
		values.insert(v);
	}

	statement.finalize();
	return !values.empty();
}

void Store::run(void)
{
	Synchronize(this);

	const double maxAge = 2*3600.;	// TODO
	const double delay = 0.1;	// TODO
	const int batch = 10;		// TODO

	BinaryString node;
	DesynchronizeStatement(this, node = Network::Instance->overlay()->localNode());

	LogDebug("Store::run", "Started");

	// Delete old non-permanent values
	Database::Statement statement = mDatabase->prepare("DELETE FROM map WHERE type != ?1 AND time < ?2");
	statement.bind(1, static_cast<int>(Permanent));
	statement.bind(2, Time::Now() - maxAge);
	statement.execute();

	try {
		// Publish everything into DHT periodically
		int offset = 0;
		while(true)
		{
			int n;
			DesynchronizeStatement(this, n = Network::Instance->overlay()->connectionsCount());
			if(n == 0)
			{
				LogDebug("Store::run", "Interrupted");
				return;
			}

			// Select DHT values
			Database::Statement statement = mDatabase->prepare("SELECT digest FROM blocks WHERE digest IS NOT NULL ORDER BY digest LIMIT ?1 OFFSET ?2");
			statement.bind(1, batch);
			statement.bind(2, offset);

			List<BinaryString> result;
			statement.fetchColumn(0, result);
			statement.finalize();

			if(result.empty()) break;
			else {
				Desynchronize(this);

				for(List<BinaryString>::iterator it = result.begin(); it != result.end(); ++it)
					Network::Instance->storeValue(*it, node);
			}

			offset+= result.size();
			Thread::Sleep(delay);
		}

		LogDebug("Store::run", "Finished, " + String::number(offset) + " values published");
	}
	catch(const std::exception &e)
	{
		LogWarn("Store::run", e.what());
	}
}

}
<|endoftext|>


<commit_before>/***************************************************************************
*  @file       shared_pointer.hpp
*  @author     Yue Wang
*  @date       04  Feb 2014
*                  Jul 2015
*                  Oct 2015
*  @remark     This code is for the exercises from C++ Primer 5th Edition
*  @note
***************************************************************************/

#pragma once
#include <functional>
#include "delete.hpp"

namespace cp5
{
    template<typename T>
    class SharedPointer;

    template<typename T>
    auto swap(SharedPointer<T>& lhs, SharedPointer<T>& rhs)
    {
        using std::swap;
        swap(lhs.ptr, rhs.ptr);
        swap(lhs.ref_count, rhs.ref_count);
        swap(lhs.deleter, rhs.deleter);
    }

    template<typename T>
    class SharedPointer
    {
    public:
        //
        //  Default Ctor
        //
        SharedPointer()
            : ptr{ nullptr }, ref_count{ new std::size_t(1) }, deleter{ cp5::Delete{} }
        { }
        //
        //  Ctor that takes raw pointer
        //
        explicit SharedPointer(T* raw_ptr)
            : ptr{ raw_ptr }, ref_count{ new std::size_t(1) }, deleter{ cp5::Delete{} }
        { }
        //
        //  Copy Ctor
        //
        SharedPointer(SharedPointer const& other)
            : ptr{ other.ptr }, ref_count{ other.ref_count }, deleter{ other.deleter }
        {
            ++*ref_count;
        }
        //
        //  Move Ctor
        //
        SharedPointer(SharedPointer && other) noexcept
            : ptr{ other.ptr }, ref_count{ other.ref_count }, deleter{ std::move(other.deleter) }
        {
            other.ptr = nullptr;
            other.ref_count = nullptr;
        }
        //
        //  Copy assignment
        //
        SharedPointer& operator=(SharedPointer const& rhs)
        {
            //increment first to ensure safty for self-assignment
            ++*rhs.ref_count;
            decrement_and_destroy();
            ptr = rhs.ptr, ref_count = rhs.ref_count, deleter = rhs.deleter;
            return *this;
        }
        //
        //  Move assignment
        //
        SharedPointer& operator=(SharedPointer && rhs) noexcept
        {
            cp5::swap(*this, rhs);
            rhs.decrement_and_destroy();
            return *this;
        }
        //
        //  Conversion operator
        //
        operator bool() const
        {
            return ptr ? true : false;
        }
        //
        //  Dereference
        //
        T& operator* () const
        {
            return *ptr;
        }
        //
        //  Arrow
        //
        T* operator->() const
        {
            return &*ptr;
        }
        //
        //  Use count
        //
        auto use_count() const
        {
            return *ref_count;
        }
        //
        //  Get underlying pointer
        //
        auto get() const
        {
            return ptr;
        }
        //
        //  Check if the unique user
        //
        auto unique() const
        {
            return 1 == *refCount;
        }
        //
        //  Swap
        //
        auto swap(SharedPointer& rhs)
        {
            ::swap(*this, rhs);
        }
        //
        // Free the object pointed to, if unique
        //
        auto reset()
        {
            decrement_and_destroy();
        }
        //
        // Reset with the new raw pointer
        //
        auto reset(T* pointer)
        {
            if (ptr != pointer)
            {
                decrement_n_destroy();
                ptr = pointer;
                ref_count = new std::size_t(1);
            }
        }
        //
        //  Reset with raw pointer and deleter
        //
        auto reset(T *pointer, const std::function<void(T*)>& d)
        {
            reset(pointer);
            deleter = d;
        }
        //
        //  Dtor
        //
        ~SharedPointer()
        {
            decrement_and_destroy();
        }
    private:
        T* ptr;
        std::size_t* ref_count;
        std::function<void(T*)> deleter;

        auto decrement_and_destroy()
        {
            if (ptr && 0 == --*ref_count)
                delete ref_count,
                deleter(ptr);
            else if (!ptr)
                delete ref_count;
            ref_count = nullptr;
            ptr = nullptr;
        }
    };
}//namespace<commit_msg>Update shared_pointer.hpp<commit_after>/***************************************************************************
*  @file       shared_pointer.hpp
*  @author     Yue Wang
*  @date       04  Feb 2014
*                  Jul 2015
*                  Oct 2015
*  @remark     This code is for the exercises from C++ Primer 5th Edition
*  @note
***************************************************************************/

#pragma once
#include <functional>
#include "delete.hpp"

namespace cp5
{
    template<typename T>
    class SharedPointer;

    template<typename T>
    auto swap(SharedPointer<T>& lhs, SharedPointer<T>& rhs)
    {
        using std::swap;
        swap(lhs.ptr, rhs.ptr);
        swap(lhs.ref_count, rhs.ref_count);
        swap(lhs.deleter, rhs.deleter);
    }

    template<typename T>
    class SharedPointer
    {
    public:
        //
        //  Default Ctor
        //
        SharedPointer()
            : ptr{ nullptr }, ref_count{ new std::size_t(1) }, deleter{ cp5::Delete{} }
        { }
        //
        //  Ctor that takes raw pointer
        //
        explicit SharedPointer(T* raw_ptr)
            : ptr{ raw_ptr }, ref_count{ new std::size_t(1) }, deleter{ cp5::Delete{} }
        { }
        //
        //  Copy Ctor
        //
        SharedPointer(SharedPointer const& other)
            : ptr{ other.ptr }, ref_count{ other.ref_count }, deleter{ other.deleter }
        {
            ++*ref_count;
        }
        //
        //  Move Ctor
        //
        SharedPointer(SharedPointer && other) noexcept
            : ptr{ other.ptr }, ref_count{ other.ref_count }, deleter{ std::move(other.deleter) }
        {
            other.ptr = nullptr;
            other.ref_count = nullptr;
        }
        //
        //  Copy assignment
        //
        SharedPointer& operator=(SharedPointer const& rhs)
        {
            //increment first to ensure safty for self-assignment
            ++*rhs.ref_count;
            decrement_and_destroy();
            ptr = rhs.ptr, ref_count = rhs.ref_count, deleter = rhs.deleter;
            return *this;
        }
        //
        //  Move assignment
        //
        SharedPointer& operator=(SharedPointer && rhs) noexcept
        {
            cp5::swap(*this, rhs);
            rhs.decrement_and_destroy();
            return *this;
        }
        //
        //  Conversion operator
        //
        operator bool() const
        {
            return ptr ? true : false;
        }
        //
        //  Dereference
        //
        T& operator* () const
        {
            return *ptr;
        }
        //
        //  Arrow
        //
        T* operator->() const
        {
            return &*ptr;
        }
        //
        //  Use count
        //
        auto use_count() const
        {
            return *ref_count;
        }
        //
        //  Get underlying pointer
        //
        auto get() const
        {
            return ptr;
        }
        //
        //  Check if the unique user
        //
        auto unique() const
        {
            return 1 == *refCount;
        }
        //
        //  Swap
        //
        auto swap(SharedPointer& rhs)
        {
            ::swap(*this, rhs);
        }
        //
        // Free the object pointed to, if unique
        //
        auto reset()
        {
            decrement_and_destroy();
        }
        //
        // Reset with the new raw pointer
        //
        auto reset(T* pointer)
        {
            if (ptr != pointer)
            {
                decrement_n_destroy();
                ptr = pointer;
                ref_count = new std::size_t(1);
            }
        }
        //
        //  Reset with raw pointer and deleter
        //
        auto reset(T *pointer, const std::function<void(T*)>& d)
        {
            reset(pointer);
            deleter = d;
        }
        //
        //  Dtor
        //
        ~SharedPointer()
        {
            decrement_and_destroy();
        }
    private:
        T* ptr;
        std::size_t* ref_count;
        std::function<void(T*)> deleter;

        auto decrement_and_destroy()
        {
            if (ptr && 0 == --*ref_count){
                delete ref_count,
                deleter(ptr);
            }
            else if (!ptr)
                delete ref_count;
            ref_count = nullptr;
            ptr = nullptr;
        }
    };
}//namespace
<|endoftext|>