protohackers

My solutions to the protohackers.com challenges.
git clone git://henryandlizzy.uk/protohackers
Log | Files | Refs

commit 3579fe196cf9399a86a275fa264bd0c0f7d5d44b
parent 894daa68c1e86b41d03d041bd550eef059fcfb52
Author: Henry Wilson <henry@henryandlizzy.uk>
Date:   Wed, 30 Aug 2023 17:54:29 +0000

General improvements to protohack-0

Diffstat:
Mfd.hpp | 19+++++++++++++++++--
Minet.hpp | 2+-
Mprotohack-0.cpp | 65++++++++++++++++++++++++++++++++++++-----------------------------
3 files changed, 54 insertions(+), 32 deletions(-)

diff --git a/fd.hpp b/fd.hpp @@ -1,7 +1,9 @@ #pragma once #include <unistd.h> +#include <err.h> #include <string> +#include <span> struct descriptor { @@ -59,9 +61,11 @@ private: int fd; }; -int write(descriptor& d, std::string_view s) +ssize_t write(int d, std::string_view s) { - return ::write(d, s.data(), s.size()); + if (ssize_t n = ::write(d, s.data(), s.size()); n != -1) + return n; + ::err(1, "write"); } std::string read(descriptor& d) @@ -73,3 +77,14 @@ std::string read(descriptor& d) else return {}; } + +std::string_view read(int d, std::span<char> buf) +{ + ssize_t n = ::read(d, buf.data(), buf.size()); + if ((n < 0) or ((size_t)n > buf.size())) + err(1, "read"); + else if (n) + return {buf.data(), (size_t)n}; + else + return {}; +} diff --git a/inet.hpp b/inet.hpp @@ -14,7 +14,7 @@ namespace inet { descriptor listen(int type) { - struct sockaddr_in addr {sizeof addr, AF_INET, htons(1337), INADDR_ANY}; + struct sockaddr_in addr {sizeof addr, AF_INET, htons(1337), {INADDR_ANY}, {}}; int reuseaddr = 1; descriptor incoming{::socket(AF_INET, type, 0)}; diff --git a/protohack-0.cpp b/protohack-0.cpp @@ -2,61 +2,68 @@ #include <poll.h> +#include <algorithm> #include <iostream> -#include <sstream> -#include <span> #include <map> -#include <vector> -struct client +int poll(std::span<struct pollfd> pfds, int timeout = INFTIM) { - descriptor socket; - std::string buffer; -}; + if (int n = ::poll(pfds.data(), pfds.size(), timeout); n != -1) + return n; + err(1, "poll"); +} int main() { - std::map<int, client> clients; - std::vector<pollfd> awaitables; - auto incoming = inet::listen(SOCK_STREAM); + std::map<int, descriptor> clients; + auto incoming_socket = inet::listen(SOCK_STREAM); + std::cout << "Protohack 0: Smoke Test\n"; for (;;) { - awaitables.clear(); - awaitables.push_back(pollfd{incoming, POLLIN, 0}); + size_t const pollen = clients.size() + 1; + pollfd events_buf[pollen]; + std::span events{events_buf, pollen}; + + auto& socket_event = events.front(); + auto client_events = events.subspan(1); + + socket_event = pollfd{incoming_socket, POLLIN, 0}; - for (auto const& c : clients) - awaitables.push_back(pollfd{c.second.socket, POLLIN, 0}); + std::transform(clients.cbegin(), clients.cend(), client_events.begin(), [](auto& c) { + return pollfd{c.second, POLLIN, 0}; + }); - if (::poll(awaitables.data(), awaitables.size(), INFTIM) == -1) - err(1, "poll"); + int ready = poll(events); - if (awaitables.front().revents & POLLIN) // incoming + if (socket_event.revents & POLLIN) { - auto conn = inet::accept(incoming); - clients[conn] = {std::move(conn), {}}; + auto conn = inet::accept(incoming_socket); + clients[conn] = std::move(conn); + if (not --ready) + continue; } - for (auto& ev : std::span{awaitables}.subspan(1)) + for (auto& ev : client_events) { if (~ev.revents & POLLIN) continue; - char buf[0x1000]; - int n = ::read(ev.fd, buf, sizeof buf); - if (not n) + char data_buf[0x1000]; + auto data = read(ev.fd, std::span{data_buf}); + + if (data.empty()) { std::cout << "* " << ev.fd << " disconnected\n"; clients.erase(ev.fd); continue; } - if (n < 0 or n > sizeof buf) - err(1, "read"); - std::cout << "* " << ev.fd << ": " << n << " bytes\n"; - ::write(1, buf, n); - //if (n != ::write(ev.fd, buf, n)) - // err(1, "write"); + std::cout << "* " << ev.fd << ": " << data.size() << " bytes\n"; + write(ev.fd, data); + + if (not --ready) + break; } } }