File net.h

File List > coroutine > net.h

Go to the documentation of this file.

#pragma once
#ifndef COROUTINE_NET_IO_H
#define COROUTINE_NET_IO_H
#include <gsl/gsl>

#include <coroutine/return.h>

#if __has_include(<WinSock2.h>) // use winsock
#include <WS2tcpip.h>
#include <WinSock2.h>
#include <ws2def.h>

static constexpr bool is_winsock = true;
static constexpr bool is_netinet = false;

using io_control_block = OVERLAPPED;

#elif __has_include(<netinet/in.h>) // use netinet
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include <unistd.h>

static constexpr bool is_winsock = false;
static constexpr bool is_netinet = true;

struct io_control_block {
    uint64_t internal;      // uint32_t errc, int32_t flag
    uint64_t internal_high; // int64_t len, socklen_t addrlen
    union {
        struct {
            int32_t offset;
            int32_t offset_high;
        };
        void* ptr; // sockaddr* addr;
    };
    int64_t handle; // int64_t sd;
};

#endif // winsock || netinet

namespace coro {

using io_buffer_t = gsl::span<std::byte>;
static_assert(sizeof(io_buffer_t) <= sizeof(void*) * 2);

class io_work_t : public io_control_block {
  public:
    coroutine_handle<void> task{};
    io_buffer_t buffer{};

  protected:
    bool ready() const noexcept;

  public:
    uint32_t error() const noexcept;
};
static_assert(sizeof(io_work_t) <= 56);

class io_send_to final : public io_work_t {
  private:
    void suspend(coroutine_handle<void> t) noexcept(false);
    int64_t resume() noexcept;

  public:
    bool await_ready() const noexcept {
        return this->ready();
    }
    void await_suspend(coroutine_handle<void> t) noexcept(false) {
        return this->suspend(t);
    }
    int64_t await_resume() noexcept {
        return this->resume();
    }
};
static_assert(sizeof(io_send_to) == sizeof(io_work_t));

class io_recv_from final : public io_work_t {
  private:
    void suspend(coroutine_handle<void> t) noexcept(false);
    int64_t resume() noexcept;

  public:
    bool await_ready() const noexcept {
        return this->ready();
    }
    void await_suspend(coroutine_handle<void> t) noexcept(false) {
        return this->suspend(t);
    }
    int64_t await_resume() noexcept {
        return this->resume();
    }
};
static_assert(sizeof(io_recv_from) == sizeof(io_work_t));

class io_send final : public io_work_t {
  private:
    void suspend(coroutine_handle<void> t) noexcept(false);
    int64_t resume() noexcept;

  public:
    bool await_ready() const noexcept {
        return this->ready();
    }
    void await_suspend(coroutine_handle<void> t) noexcept(false) {
        return this->suspend(t);
    }
    int64_t await_resume() noexcept {
        return this->resume();
    }
};
static_assert(sizeof(io_send) == sizeof(io_work_t));

class io_recv final : public io_work_t {
  private:
    void suspend(coroutine_handle<void> t) noexcept(false);

    int64_t resume() noexcept;

  public:
    bool await_ready() const noexcept {
        return this->ready();
    }
    void await_suspend(coroutine_handle<void> t) noexcept(false) {
        return this->suspend(t);
    }
    int64_t await_resume() noexcept {
        return this->resume();
    }
};
static_assert(sizeof(io_recv) == sizeof(io_work_t));

auto send_to(uint64_t sd, const sockaddr_in& remote, io_buffer_t buf,
             io_work_t& work) noexcept(false) -> io_send_to&;

auto send_to(uint64_t sd, const sockaddr_in6& remote, io_buffer_t buf,
             io_work_t& work) noexcept(false) -> io_send_to&;

auto recv_from(uint64_t sd, sockaddr_in& remote, io_buffer_t buf,
               io_work_t& work) noexcept(false) -> io_recv_from&;

auto recv_from(uint64_t sd, sockaddr_in6& remote, io_buffer_t buf,
               io_work_t& work) noexcept(false) -> io_recv_from&;

auto send_stream(uint64_t sd, io_buffer_t buf, uint32_t flag,
                 io_work_t& work) noexcept(false) -> io_send&;

auto recv_stream(uint64_t sd, io_buffer_t buf, uint32_t flag,
                 io_work_t& work) noexcept(false) -> io_recv&;

void poll_net_tasks(uint64_t nano) noexcept(false);

uint32_t get_address(const addrinfo& hint, //
                     gsl::czstring<> host, gsl::czstring<> serv,
                     gsl::span<sockaddr_in> output) noexcept;

uint32_t get_address(const addrinfo& hint, //
                     gsl::czstring<> host, gsl::czstring<> serv,
                     gsl::span<sockaddr_in6> output) noexcept;

uint32_t get_name(const sockaddr_in& addr, //
                  gsl::zstring<NI_MAXHOST> name, gsl::zstring<NI_MAXSERV> serv,
                  int32_t flags = NI_NUMERICHOST | NI_NUMERICSERV) noexcept;

uint32_t get_name(const sockaddr_in6& addr, //
                  gsl::zstring<NI_MAXHOST> name, gsl::zstring<NI_MAXSERV> serv,
                  int32_t flags = NI_NUMERICHOST | NI_NUMERICSERV) noexcept;

} // namespace coro

#endif // COROUTINE_NET_IO_H