2 * nghttp2 - HTTP/2 C Library
4 * Copyright (c) 2014 Tatsuhiro Tsujikawa
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 #include "nghttp2_config.h"
32 /* Structure for scatter/gather I/O. */
34 void *iov_base; /* Pointer to data. */
35 size_t iov_len; /* Length of data. */
53 #define DEFAULT_WR_IOVCNT 16
55 #if defined(IOV_MAX) && IOV_MAX < DEFAULT_WR_IOVCNT
56 # define MAX_WR_IOVCNT IOV_MAX
57 #else // !defined(IOV_MAX) || IOV_MAX >= DEFAULT_WR_IOVCNT
58 # define MAX_WR_IOVCNT DEFAULT_WR_IOVCNT
59 #endif // !defined(IOV_MAX) || IOV_MAX >= DEFAULT_WR_IOVCNT
61 template <size_t N> struct Memchunk {
62 Memchunk(Memchunk *next_chunk)
63 : pos(std::begin(buf)), last(pos), knext(next_chunk), next(nullptr) {}
64 size_t len() const { return last - pos; }
65 size_t left() const { return std::end(buf) - last; }
66 void reset() { pos = last = std::begin(buf); }
67 std::array<uint8_t, N> buf;
71 static const size_t size = N;
74 template <typename T> struct Pool {
75 Pool() : pool(nullptr), freelist(nullptr), poolsize(0) {}
80 freelist = freelist->next;
96 for (auto p = pool; p;) {
97 auto knext = p->knext;
104 using value_type = T;
110 template <typename Memchunk> struct Memchunks {
111 Memchunks(Pool<Memchunk> *pool)
112 : pool(pool), head(nullptr), tail(nullptr), len(0) {}
113 Memchunks(const Memchunks &) = delete;
114 Memchunks(Memchunks &&other) noexcept
115 : pool{other.pool}, // keep other.pool
116 head{std::exchange(other.head, nullptr)},
117 tail{std::exchange(other.tail, nullptr)},
118 len{std::exchange(other.len, 0)} {}
119 Memchunks &operator=(const Memchunks &) = delete;
120 Memchunks &operator=(Memchunks &&other) noexcept {
121 if (this == &other) {
128 head = std::exchange(other.head, nullptr);
129 tail = std::exchange(other.tail, nullptr);
130 len = std::exchange(other.len, 0);
138 for (auto m = head; m;) {
144 size_t append(char c) {
146 head = tail = pool->get();
147 } else if (tail->left() == 0) {
148 tail->next = pool->get();
155 size_t append(const void *src, size_t count) {
160 auto first = static_cast<const uint8_t *>(src);
161 auto last = first + count;
164 head = tail = pool->get();
168 auto n = std::min(static_cast<size_t>(last - first), tail->left());
169 tail->last = std::copy_n(first, n, tail->last);
176 tail->next = pool->get();
182 template <size_t N> size_t append(const char (&s)[N]) {
183 return append(s, N - 1);
185 size_t append(const std::string &s) { return append(s.c_str(), s.size()); }
186 size_t append(const StringRef &s) { return append(s.c_str(), s.size()); }
187 size_t append(const ImmutableString &s) {
188 return append(s.c_str(), s.size());
190 size_t copy(Memchunks &dest) {
193 dest.append(m->pos, m->len());
198 size_t remove(void *dest, size_t count) {
199 if (!tail || count == 0) {
203 auto first = static_cast<uint8_t *>(dest);
204 auto last = first + count;
210 auto n = std::min(static_cast<size_t>(last - first), m->len());
213 first = std::copy_n(m->pos, n, first);
223 if (head == nullptr) {
227 return first - static_cast<uint8_t *>(dest);
229 size_t remove(Memchunks &dest, size_t count) {
230 if (!tail || count == 0) {
239 auto n = std::min(left, m->len());
242 dest.append(m->pos, n);
253 if (head == nullptr) {
259 size_t remove(Memchunks &dest) {
260 assert(pool == dest.pool);
262 if (head == nullptr) {
268 if (dest.tail == nullptr) {
271 dest.tail->next = head;
277 head = tail = nullptr;
282 size_t drain(size_t count) {
287 auto n = std::min(count, m->len());
299 if (head == nullptr) {
302 return ndata - count;
304 int riovec(struct iovec *iov, int iovcnt) const {
310 for (i = 0; i < iovcnt && m; ++i, m = m->next) {
311 iov[i].iov_base = m->pos;
312 iov[i].iov_len = m->len();
316 size_t rleft() const { return len; }
318 for (auto m = head; m;) {
324 head = tail = nullptr;
327 Pool<Memchunk> *pool;
328 Memchunk *head, *tail;
332 // Wrapper around Memchunks to offer "peeking" functionality.
333 template <typename Memchunk> struct PeekMemchunks {
334 PeekMemchunks(Pool<Memchunk> *pool)
341 PeekMemchunks(const PeekMemchunks &) = delete;
342 PeekMemchunks(PeekMemchunks &&other) noexcept
343 : memchunks{std::move(other.memchunks)},
344 cur{std::exchange(other.cur, nullptr)},
345 cur_pos{std::exchange(other.cur_pos, nullptr)},
346 cur_last{std::exchange(other.cur_last, nullptr)},
347 len{std::exchange(other.len, 0)},
348 peeking{std::exchange(other.peeking, true)} {}
349 PeekMemchunks &operator=(const PeekMemchunks &) = delete;
350 PeekMemchunks &operator=(PeekMemchunks &&other) noexcept {
351 if (this == &other) {
355 memchunks = std::move(other.memchunks);
356 cur = std::exchange(other.cur, nullptr);
357 cur_pos = std::exchange(other.cur_pos, nullptr);
358 cur_last = std::exchange(other.cur_last, nullptr);
359 len = std::exchange(other.len, 0);
360 peeking = std::exchange(other.peeking, true);
364 size_t append(const void *src, size_t count) {
365 count = memchunks.append(src, count);
369 size_t remove(void *dest, size_t count) {
371 count = memchunks.remove(dest, count);
376 if (count == 0 || len == 0) {
381 cur = memchunks.head;
385 // cur_last could be updated in append
386 cur_last = cur->last;
388 if (cur_pos == cur_last) {
393 auto first = static_cast<uint8_t *>(dest);
394 auto last = first + count;
397 auto n = std::min(last - first, cur_last - cur_pos);
399 first = std::copy_n(cur_pos, n, first);
406 assert(cur_pos == cur_last);
412 cur_last = cur->last;
414 return first - static_cast<uint8_t *>(dest);
416 size_t rleft() const { return len; }
417 size_t rleft_buffered() const { return memchunks.rleft(); }
418 void disable_peek(bool drain) {
423 auto n = rleft_buffered() - rleft();
425 assert(len == memchunks.rleft());
427 len = memchunks.rleft();
430 cur_pos = cur_last = nullptr;
436 cur_pos = cur_last = nullptr;
440 Memchunks<Memchunk> memchunks;
441 // Pointer to the Memchunk currently we are reading/writing.
443 // Region inside cur, we have processed to cur_pos.
444 uint8_t *cur_pos, *cur_last;
445 // This is the length we have left unprocessed. len <=
446 // memchunk.rleft() must hold.
448 // true if peeking is enabled. Initially it is true.
452 using Memchunk16K = Memchunk<16_k>;
453 using MemchunkPool = Pool<Memchunk16K>;
454 using DefaultMemchunks = Memchunks<Memchunk16K>;
455 using DefaultPeekMemchunks = PeekMemchunks<Memchunk16K>;
457 inline int limit_iovec(struct iovec *iov, int iovcnt, size_t max) {
461 for (int i = 0; i < iovcnt; ++i) {
462 auto d = std::min(max, iov[i].iov_len);
472 // MemchunkBuffer is similar to Buffer, but it uses pooled Memchunk
473 // for its underlying buffer.
474 template <typename Memchunk> struct MemchunkBuffer {
475 MemchunkBuffer(Pool<Memchunk> *pool) : pool(pool), chunk(nullptr) {}
476 MemchunkBuffer(const MemchunkBuffer &) = delete;
477 MemchunkBuffer(MemchunkBuffer &&other) noexcept
478 : pool(other.pool), chunk(other.chunk) {
479 other.chunk = nullptr;
481 MemchunkBuffer &operator=(const MemchunkBuffer &) = delete;
482 MemchunkBuffer &operator=(MemchunkBuffer &&other) noexcept {
483 if (this == &other) {
490 other.chunk = nullptr;
496 if (!pool || !chunk) {
499 pool->recycle(chunk);
502 // Ensures that the underlying buffer is allocated.
503 void ensure_chunk() {
510 // Releases the underlying buffer.
511 void release_chunk() {
515 pool->recycle(chunk);
519 // Returns true if the underlying buffer is allocated.
520 bool chunk_avail() const { return chunk != nullptr; }
522 // The functions below must be called after the underlying buffer is
523 // allocated (use ensure_chunk).
525 // MemchunkBuffer provides the same interface functions with Buffer.
526 // Since we has chunk as a member variable, pos and last are
527 // implemented as wrapper functions.
529 uint8_t *pos() const { return chunk->pos; }
530 uint8_t *last() const { return chunk->last; }
532 size_t rleft() const { return chunk->len(); }
533 size_t wleft() const { return chunk->left(); }
534 size_t write(const void *src, size_t count) {
535 count = std::min(count, wleft());
536 auto p = static_cast<const uint8_t *>(src);
537 chunk->last = std::copy_n(p, count, chunk->last);
540 size_t write(size_t count) {
541 count = std::min(count, wleft());
542 chunk->last += count;
545 size_t drain(size_t count) {
546 count = std::min(count, rleft());
550 size_t drain_reset(size_t count) {
551 count = std::min(count, rleft());
552 std::copy(chunk->pos + count, chunk->last, std::begin(chunk->buf));
553 chunk->last = std::begin(chunk->buf) + (chunk->last - (chunk->pos + count));
554 chunk->pos = std::begin(chunk->buf);
557 void reset() { chunk->reset(); }
558 uint8_t *begin() { return std::begin(chunk->buf); }
559 uint8_t &operator[](size_t n) { return chunk->buf[n]; }
560 const uint8_t &operator[](size_t n) const { return chunk->buf[n]; }
562 Pool<Memchunk> *pool;
566 using DefaultMemchunkBuffer = MemchunkBuffer<Memchunk16K>;
568 } // namespace nghttp2