read.hpp

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#pragma once


#include <felspar/io/warden.hpp>

#include <span>

#if __has_include(<unistd.h>)
#include <unistd.h>
#endif


namespace felspar::io {

Free standing version of read_some

17
18
19
20
21
22
23
24
25
26
    template<typename S, typename B>
    inline auto read_some(
            io::warden &warden,
            S &&sock,
            B &&buffer,
            std::optional<std::chrono::nanoseconds> const timeout = {},
            felspar::source_location const &loc =
                    felspar::source_location::current()) {
        return warden.read_some(sock, buffer, timeout, loc);
    }

A read buffer that can be split up and have more information read into it over time as data appears on the file descriptor

31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
    template<typename R>
    class read_buffer {
        R storage = {};
        std::span<typename R::value_type> data_read = {storage.data(), {}};
        std::span<std::byte> empty_buffer = {
                reinterpret_cast<std::byte *>(storage.data()), storage.size()};

      public:
        using storage_type = R;
        using span_type = decltype(data_read);
        using value_type = typename R::value_type;

        read_buffer() {}

        template<typename S>
        warden::task<void> do_read_some(
                warden &ward,
                S &&sock,
                std::optional<std::chrono::nanoseconds> timeout = {},
                felspar::source_location const &loc =
                        felspar::source_location::current()) {
            std::size_t const bytes_read =
                    co_await read_some(ward, sock, remaining(), timeout, loc);
            if (bytes_read == 0) {
                throw stdexcept::runtime_error{
                        "Got a zero read, we're done", loc};
            } else {
                data_read = {data_read.data(), data_read.size() + bytes_read};
                empty_buffer = empty_buffer.subspan(bytes_read);
            }
        }

        span_type consume(std::size_t const bytes) {
            auto const start = data_read.first(bytes);
            data_read = data_read.subspan(bytes);
            return start;
        }

TODO Almost certainly remove these

70
71
72
73
74
75
76
77
78
79
80
81
        auto not_consumed() const { return data_read; }
        std::span<std::byte> remaining() {
            return {reinterpret_cast<std::byte *>(empty_buffer.data()),
                    empty_buffer.size()};
        }
        auto data() { return storage.data(); }
        auto find(value_type v) {
            return std::find(data_read.begin(), data_read.end(), v);
        }
        auto begin() { return data_read.begin(); }
        auto end() { return data_read.end(); }
    };

Issue a read request for a specific amount of data

 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
    template<typename S>
    inline warden::task<std::size_t> read_exactly(
            warden &ward,
            S &&sock,
            std::span<std::byte> b,
            std::optional<std::chrono::nanoseconds> timeout = {},
            felspar::source_location const &loc =
                    felspar::source_location::current()) {
        std::span<std::byte> in{b};
        while (in.size()) {
            auto const bytes = co_await ward.read_some(sock, in, timeout, loc);
            if (not bytes) { co_return b.size() - in.size(); }
            in = in.subspan(bytes);
        }
        co_return b.size();
    }
    template<typename S>
    inline warden::task<std::size_t> read_exactly(
            warden &w,
            S &&s,
            void *buf,
            std::size_t count,
            std::optional<std::chrono::nanoseconds> timeout = {},
            felspar::source_location const &loc =
                    felspar::source_location::current()) {
        return read_exactly(
                w, std::forward<S>(s),
                std::span<std::byte>{reinterpret_cast<std::byte *>(buf), count},
                std::move(timeout), loc);
    }
    template<typename S>
    inline warden::task<std::size_t> read_exactly(
            warden &w,
            S &&s,
            std::span<std::uint8_t> b,
            std::optional<std::chrono::nanoseconds> timeout = {},
            felspar::source_location const &loc =
                    felspar::source_location::current()) {
        return read_exactly(
                w, std::forward<S>(s),
                std::span<std::byte>{
                        reinterpret_cast<std::byte *>(b.data()), b.size()},
                std::move(timeout), loc);
    }

Read a line (up to the next LF) and strip any final CR

132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
    template<typename S, typename R>
    inline warden::task<typename R::span_type> read_until_lf_strip_cr(
            warden &ward,
            S &&sock,
            R &read_buffer,
            std::optional<std::chrono::nanoseconds> timeout = {},
            felspar::source_location const &loc =
                    felspar::source_location::current()) {
        auto cr = read_buffer.find('\n');
        while (cr == read_buffer.end()) {
            co_await read_buffer.do_read_some(ward, sock, timeout, loc);
            cr = read_buffer.find('\n');
        }
        std::size_t const line = std::distance(read_buffer.begin(), cr);
        auto read = read_buffer.consume(line + 1).first(line);
        if (read.size() and read.back() == '\r') {
            co_return read.first(read.size() - 1);
        } else {
            co_return read;
        }
    }


}