ESPHome 2026.9.0
Loading...
Searching...
No Matches
api_overflow_buffer.cpp
Go to the documentation of this file.
2#ifdef USE_API
3#include <cstring>
4
5namespace esphome::api {
6
8 // Nested call from inside socket->write(); see draining_
9 if (this->draining_)
10 return 0;
11
12 struct DrainGuard {
13 APIOverflowBuffer &owner;
14 ~DrainGuard() { this->owner.draining_ = false; }
15 } guard{*this};
16 this->draining_ = true;
17
18 while (this->count_ > 0) {
19 uint8_t *msg = this->buf_.data() + this->head_;
20 size_t len = msg[0] | (msg[1] << 8);
21
22 ssize_t sent = socket->write(msg + LEN_PREFIX, len);
23 if (sent <= 0)
24 return sent;
25 if (static_cast<size_t>(sent) < len) {
26 // Step past the sent bytes and rewrite the prefix there; it lands on bytes already sent
27 this->head_ += sent;
28 len -= sent;
29 msg += sent;
30 msg[0] = len;
31 msg[1] = len >> 8;
32 return sent;
33 }
34 this->head_ += LEN_PREFIX + len;
35 this->count_--;
36 }
37
38 this->head_ = 0;
39 if (this->release_when_drained_) {
40 this->release_when_drained_ = false;
41 this->buf_.release();
42 } else {
43 this->buf_.clear();
44 }
45 return 0;
46}
47
48bool APIOverflowBuffer::enqueue_iov(const struct iovec *iov, int iovcnt, size_t total_len, size_t skip) {
49 if (this->count_ >= API_MAX_SEND_QUEUE)
50 return false;
51
52 const size_t new_len = total_len - skip;
53 const size_t new_bytes = LEN_PREFIX + new_len;
54 const size_t live = this->buf_.size() - this->head_;
55 // A lone message is only bound by the buffer; refusing it would just drop the connection
56 if (live + new_bytes > (this->count_ > 0 ? MAX_BYTES : MAX_LONE_BYTES))
57 return false;
58
59 if (this->buf_.size() + new_bytes > this->buf_.capacity()) {
60 // Storage would move under an outer drain's write()
61 if (this->draining_)
62 return false;
63 if (this->head_ > 0) {
64 // Reclaim the sent prefix before growing
65 this->buf_.drop_front(this->head_);
66 this->head_ = 0;
67 }
68 if (!this->buf_.reserve(reserve_for(live + new_bytes)))
69 return false;
70 }
71
72 uint8_t *dst = this->buf_.append(new_bytes);
73 if (dst == nullptr)
74 return false;
75 dst[0] = new_len;
76 dst[1] = new_len >> 8;
77 dst += LEN_PREFIX;
78 for (const struct iovec *end = iov + iovcnt; iov != end; iov++) {
79 if (skip >= iov->iov_len) {
80 skip -= iov->iov_len;
81 } else {
82 const size_t len = iov->iov_len - skip;
83 std::memcpy(dst, static_cast<const uint8_t *>(iov->iov_base) + skip, len);
84 dst += len;
85 skip = 0;
86 }
87 }
88
89 this->count_++;
90 return true;
91}
92
93} // namespace esphome::api
94
95#endif // USE_API
void drop_front(size_t drop)
Drop the first drop bytes, sliding the rest down. Precondition: drop <= size().
Definition api_buffer.h:46
void release()
Release all memory (equivalent to std::vector swap trick).
Definition api_buffer.h:61
uint8_t * append(size_t n)
Grow by n bytes; returns the new bytes, or nullptr on allocation failure.
size_t size() const
Definition api_buffer.h:55
bool reserve(size_t n) ESPHOME_ALWAYS_INLINE
Returns false if allocation fails; the buffer is left unchanged.
Definition api_buffer.h:31
TCP send backlog, only used when the socket send buffer is full.
bool enqueue_iov(const struct iovec *iov, int iovcnt, size_t total_len, size_t skip)
Queue iov data from byte offset skip as one message.
static constexpr size_t LEN_PREFIX
static constexpr size_t MAX_BYTES
static constexpr size_t reserve_for(size_t want)
ssize_t try_drain(socket::Socket *socket)
Drain queued messages to the socket.
static constexpr size_t MAX_LONE_BYTES
__int64 ssize_t
Definition httplib.h:178
const void size_t len
Definition hal.h:64
void * iov_base
Definition headers.h:103
size_t iov_len
Definition headers.h:104
uint8_t end[39]
Definition sun_gtil2.cpp:17