userver: userver/utils/iovec_advance.hpp Source File
Loading...
Searching...
No Matches
iovec_advance.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/utils/iovec_advance.hpp
4/// @brief Helpers for advancing `iovec` buffers used in scatter/gather I/O
5/// (`readv`/`writev`).
6
7#include <sys/uio.h>
8#include <cstddef>
9
10#include <userver/utils/assert.hpp>
11
12USERVER_NAMESPACE_BEGIN
13
14namespace utils {
15
16/// @ingroup userver_universal
17///
18/// @brief Trims the first `n` bytes off a single `iovec`: moves `iov_base`
19/// forward and decreases `iov_len` by `n`.
20///
21/// Typically called after a partial transfer to adjust the buffer for the
22/// next I/O call.
23///
24/// @param iov the buffer to trim.
25/// @param n number of bytes already transferred.
26///
27/// @warning `n` must be strictly less than `iov.iov_len`, so that the resulting
28/// buffer is never empty. The precondition is only validated by `UASSERT`, i.e.
29/// in debug builds; violating it in a release build yields a buffer pointing
30/// outside of the original memory region.
31inline void Advance(struct iovec& iov, std::size_t n) {
32 UASSERT(n < iov.iov_len);
33 iov.iov_base = static_cast<char*>(iov.iov_base) + n;
34 iov.iov_len -= n;
35}
36
37/// @ingroup userver_universal
38///
39/// @brief A cursor over an array of `iovec` entries, used to track progress
40/// across multiple scatter/gather buffers.
41struct IovIter {
42 /// Points to the first entry that has not been transferred completely.
43 /// May point past the end of the array when `iov_size == 0`, so it must not
44 /// be dereferenced without checking `iov_size` first.
45 const struct iovec* iov{};
46
47 /// Number of `iovec` entries remaining, starting at `iov`.
48 std::size_t iov_size{};
49
50 /// Byte offset within `*iov`, i.e. the number of bytes of the current entry
51 /// that were already transferred. `0` means the current entry has not been
52 /// partially consumed and the list can be reused as is.
53 std::size_t iov_offset{0};
54};
55
56/// @ingroup userver_universal
57///
58/// @brief Advances the iterator by `n` transferred bytes across the `iovec`
59/// array.
60///
61/// After the call:
62/// - `iov_size == 0` means that everything was transferred; `iov` points past
63/// the end of the array and must not be dereferenced;
64/// - `iov_offset == 0` with a non-zero `iov_size` means the iterator points
65/// exactly at the start of the next unconsumed entry, so `{iov, iov_size}`
66/// can be passed to the next I/O call as is;
67/// - a non-zero `iov_offset` means that `*iov` is partially consumed and has to
68/// be trimmed by `iov_offset` bytes (e.g. via
69/// utils::Advance(struct iovec&, std::size_t)) before reuse. The entries
70/// themselves are never modified by this overload, as they are pointed to by
71/// a pointer-to-const.
72inline void Advance(IovIter& iter, std::size_t n) {
73 while (0 < iter.iov_size) {
74 const std::size_t iov_len = iter.iov->iov_len;
75 if (iov_len <= n) {
76 ++iter.iov;
77 --iter.iov_size;
78 n -= iov_len;
79 UASSERT(0 < iter.iov_size || 0 == n);
80 } else [[unlikely]] {
81 iter.iov_offset = n;
82 return;
83 }
84 }
85}
86
87} // namespace utils
88
89USERVER_NAMESPACE_END