Coverage Report

Created: 2026-09-03 06:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pistache/include/pistache/stream.h
Line
Count
Source
1
/*
2
 * SPDX-FileCopyrightText: 2015 Mathieu Stefani
3
 *
4
 * SPDX-License-Identifier: Apache-2.0
5
 */
6
7
/* stream.h
8
   Mathieu Stefani, 05 September 2015
9
10
   A set of classes to control input over a sequence of bytes
11
*/
12
13
#pragma once
14
15
#include <pistache/os.h>
16
17
#include <cstddef>
18
#include <cstring>
19
#include <iostream>
20
#include <limits>
21
#include <stdexcept>
22
#include <streambuf>
23
#include <string>
24
#include <vector>
25
26
namespace Pistache
27
{
28
29
    static constexpr char CR = 0xD;
30
    static constexpr char LF = 0xA;
31
32
    template <typename CharT = char>
33
    class StreamBuf : public std::basic_streambuf<CharT>
34
    {
35
    public:
36
        typedef std::basic_streambuf<CharT> Base;
37
        typedef typename Base::traits_type traits_type;
38
39
#ifdef __MINGW32__
40
        // As of Oct/2024, if we do not explicitly set the locale, as below,
41
        // the test http_parsing_test.parser_reset generates an exception in
42
        // the locale destructor when the Http::RequestParser (which inherits
43
        // from StreamBuf) that the test uses goes out of scope. The locale
44
        // instance that crashes on destruction is a component of
45
        // std::basic_streambuf, inheried by StreamBuf and thence by
46
        // Http::RequestParser. This may be a bug in the mingw standard
47
        // library; setting the std::basic_streambuf's locale explicitly works
48
        // around the issue.
49
        StreamBuf()
50
            {
51
                std::locale dummy_loc("C");
52
                Base::pubimbue(dummy_loc);
53
            }
54
#endif
55
56
        void setArea(char* begin, char* current, char* end)
57
8.28k
        {
58
8.28k
            this->setg(begin, current, end);
59
8.28k
        }
60
61
1.44M
        CharT* begptr() const { return this->eback(); }
62
63
2.59M
        CharT* curptr() const { return this->gptr(); }
64
65
1.16M
        CharT* endptr() const { return this->egptr(); }
66
67
2.59M
        size_t position() const { return this->gptr() - this->eback(); }
68
69
0
        void reset() { this->setg(nullptr, nullptr, nullptr); }
70
71
        typename Base::int_type snext() const
72
25.7M
        {
73
25.7M
            if (this->gptr() == this->egptr())
74
0
            {
75
0
                return traits_type::eof();
76
0
            }
77
78
25.7M
            const CharT* gptr = this->gptr();
79
25.7M
            return *(gptr + 1);
80
25.7M
        }
81
    };
82
83
    template <typename CharT = char>
84
    class RawStreamBuf : public StreamBuf<CharT>
85
    {
86
    public:
87
        typedef StreamBuf<CharT> Base;
88
89
5.32k
        RawStreamBuf(char* begin, char* end) { Base::setg(begin, begin, end); }
90
        RawStreamBuf(char* begin, size_t len)
91
209k
        {
92
209k
            Base::setg(begin, begin, begin + len);
93
209k
        }
94
    };
95
96
    // Make the buffer dynamic
97
    template <typename CharT = char>
98
    class ArrayStreamBuf : public StreamBuf<CharT>
99
    {
100
    public:
101
        using Base = StreamBuf<CharT>;
102
103
        explicit ArrayStreamBuf(size_t maxSize)
104
3.22k
            : StreamBuf<CharT>()
105
3.22k
            , bytes()
106
3.22k
            , maxSize_(maxSize)
107
3.22k
        {
108
3.22k
            bytes.clear();
109
3.22k
            Base::setg(bytes.data(), bytes.data(), bytes.data() + bytes.size());
110
3.22k
        }
111
112
        template <size_t M>
113
        explicit ArrayStreamBuf(char (&arr)[M])
114
        {
115
            bytes.clear();
116
            std::copy(arr, arr + M, std::back_inserter(bytes));
117
            Base::setg(bytes.data(), bytes.data(), bytes.data() + bytes.size());
118
        }
119
120
        bool feed(const char* data, size_t len)
121
3.22k
        {
122
3.22k
            if (bytes.size() + len > maxSize_)
123
25
            {
124
25
                return false;
125
25
            }
126
            // persist current offset
127
3.19k
            size_t readOffset = static_cast<size_t>(this->gptr() - this->eback());
128
3.19k
            std::copy(data, data + len, std::back_inserter(bytes));
129
3.19k
            Base::setg(bytes.data(), bytes.data() + readOffset,
130
3.19k
                       bytes.data() + bytes.size());
131
3.19k
            return true;
132
3.22k
        }
133
134
        void reset()
135
0
        {
136
0
            std::vector<CharT> nbytes;
137
0
            bytes.swap(nbytes);
138
0
            Base::setg(bytes.data(), bytes.data(), bytes.data() + bytes.size());
139
0
        }
140
141
    private:
142
        std::vector<CharT> bytes;
143
        size_t maxSize_ = Const::MaxBuffer;
144
    };
145
146
    struct RawBuffer final
147
    {
148
0
        RawBuffer() = default;
149
        RawBuffer(std::string data, size_t length);
150
        RawBuffer(const char* data, size_t length);
151
152
0
        RawBuffer(const RawBuffer&)            = default;
153
        RawBuffer& operator=(const RawBuffer&) = default;
154
0
        RawBuffer(RawBuffer&&)                 = default;
155
        RawBuffer& operator=(RawBuffer&&)      = default;
156
157
0
        ~RawBuffer() = default;
158
159
        RawBuffer copy(size_t fromIndex = 0u) const;
160
        const std::string& data() const;
161
        size_t size() const;
162
163
    private:
164
        std::string data_;
165
        size_t length_ = 0;
166
    };
167
168
    struct FileBuffer
169
    {
170
        explicit FileBuffer(const std::string& fileName);
171
172
        int fd() const;
173
        size_t size() const;
174
175
    private:
176
        std::string fileName_;
177
        int fd_; // regular old file descriptor ("int") even in libevent case
178
        size_t size_;
179
    };
180
181
    class DynamicStreamBuf : public StreamBuf<char>
182
    {
183
    public:
184
        using Base        = StreamBuf<char>;
185
        using traits_type = typename Base::traits_type;
186
        using int_type    = typename Base::int_type;
187
188
        DynamicStreamBuf(size_t size, size_t maxSize);
189
190
        DynamicStreamBuf(const DynamicStreamBuf& other)            = delete;
191
        DynamicStreamBuf& operator=(const DynamicStreamBuf& other) = delete;
192
193
        DynamicStreamBuf(DynamicStreamBuf&& other);
194
        DynamicStreamBuf& operator=(DynamicStreamBuf&& other);
195
196
        RawBuffer buffer() const;
197
198
        void clear();
199
200
        size_t maxSize() const;
201
202
    protected:
203
        int_type overflow(int_type ch) override;
204
205
    private:
206
        void reserve(size_t size);
207
208
        std::vector<char> data_;
209
        size_t maxSize_ = Const::MaxBuffer;
210
    };
211
212
    class StreamCursor
213
    {
214
    public:
215
        explicit StreamCursor(StreamBuf<char>* _buf, size_t initialPos = 0)
216
217k
            : buf(_buf)
217
217k
        {
218
217k
            advance(initialPos);
219
217k
        }
220
221
        static constexpr int Eof = -1;
222
223
        struct Token
224
        {
225
            explicit Token(StreamCursor& _cursor)
226
1.11M
                : cursor(_cursor)
227
1.11M
                , position(cursor.buf->position())
228
1.11M
                , eback(cursor.buf->begptr())
229
1.11M
                , gptr(cursor.buf->curptr())
230
1.11M
                , egptr(cursor.buf->endptr())
231
1.11M
            { }
232
233
945k
            size_t start() const { return position; }
234
235
945k
            size_t end() const { return cursor.buf->position(); }
236
237
928k
            size_t size() const { return end() - start(); }
238
239
923k
            std::string text() const { return std::string(gptr, size()); }
240
241
8.25k
            const char* rawText() const { return gptr; }
242
243
        private:
244
            StreamCursor& cursor;
245
            size_t position;
246
            char* eback;
247
            char* gptr;
248
            char* egptr;
249
        };
250
251
        struct Revert
252
        {
253
            explicit Revert(StreamCursor& _cursor)
254
46.3k
                : cursor(_cursor)
255
46.3k
                , eback(cursor.buf->begptr())
256
46.3k
                , gptr(cursor.buf->curptr())
257
46.3k
                , egptr(cursor.buf->endptr())
258
46.3k
                , active(true)
259
46.3k
            { }
260
261
            Revert(const Revert&)            = delete;
262
            Revert& operator=(const Revert&) = delete;
263
264
            ~Revert()
265
46.3k
            {
266
46.3k
                if (active)
267
8.28k
                    revert();
268
46.3k
            }
269
270
8.28k
            void revert() { cursor.buf->setArea(eback, gptr, egptr); }
271
272
38.0k
            void ignore() { active = false; }
273
274
        private:
275
            StreamCursor& cursor;
276
            char* eback;
277
            char* gptr;
278
            char* egptr;
279
            bool active;
280
        };
281
282
        bool advance(size_t count);
283
258k
        operator size_t() const { return buf->position(); }
284
285
        bool eol() const;
286
        bool eof() const;
287
        int next() const;
288
        char current() const;
289
290
        const char* offset() const;
291
        const char* offset(size_t off) const;
292
293
        size_t diff(size_t other) const;
294
        size_t diff(const StreamCursor& other) const;
295
296
        size_t remaining() const;
297
298
        void reset();
299
300
    public:
301
        StreamBuf<char>* buf;
302
    };
303
304
    enum class CaseSensitivity { Sensitive,
305
                                 Insensitive };
306
307
    bool match_raw(const void* buf, size_t len, StreamCursor& cursor);
308
    bool match_string(const char* str, size_t len, StreamCursor& cursor,
309
                      CaseSensitivity cs = CaseSensitivity::Insensitive);
310
    template <size_t N>
311
    bool match_string(const char (&str)[N], StreamCursor& cursor,
312
                      CaseSensitivity cs = CaseSensitivity::Insensitive)
313
10.6k
    {
314
10.6k
        return match_string(str, N - 1, cursor, cs);
315
10.6k
    }
bool Pistache::match_string<6ul>(char const (&) [6ul], Pistache::StreamCursor&, Pistache::CaseSensitivity)
Line
Count
Source
313
5.32k
    {
314
5.32k
        return match_string(str, N - 1, cursor, cs);
315
5.32k
    }
bool Pistache::match_string<11ul>(char const (&) [11ul], Pistache::StreamCursor&, Pistache::CaseSensitivity)
Line
Count
Source
313
5.32k
    {
314
5.32k
        return match_string(str, N - 1, cursor, cs);
315
5.32k
    }
316
317
    bool match_literal(char c, StreamCursor& cursor,
318
                       CaseSensitivity cs = CaseSensitivity::Insensitive);
319
    bool match_until(char c, StreamCursor& cursor,
320
                     CaseSensitivity cs = CaseSensitivity::Insensitive);
321
    bool match_until(std::initializer_list<char> chars, StreamCursor& cursor,
322
                     CaseSensitivity cs = CaseSensitivity::Insensitive);
323
    bool match_double(double* val, StreamCursor& cursor);
324
325
    void skip_whitespaces(StreamCursor& cursor);
326
327
} // namespace Pistache