ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
split_buffer.cpp
Go to the documentation of this file.
1#include "split_buffer.h"
2
3#include <algorithm>
4#include <cstring>
5
7#include "esphome/core/log.h"
8
10ESPHOME_LOG_TAG(TAG, "split_buffer");
11
13
14bool SplitBuffer::init(size_t total_length, size_t max_buffer_size) {
15 this->free(); // Clean up any existing allocation
16
17 if (total_length == 0 || max_buffer_size == 0) {
18 return false;
19 }
20
21 size_t current_buffer_size = std::min(total_length, max_buffer_size);
22
23 RAMAllocator<uint8_t *> ptr_allocator;
24 RAMAllocator<uint8_t> allocator;
25
26 // Try to allocate the entire buffer first
27 while (current_buffer_size > 0) {
28 // Calculate how many buffers we need of this size
29 size_t needed_buffers = (total_length + current_buffer_size - 1) / current_buffer_size;
30
31 // Try to allocate array of buffer pointers
32 uint8_t **temp_buffers = ptr_allocator.allocate(needed_buffers);
33 if (temp_buffers == nullptr) {
34 // If we can't even allocate the pointer array, don't need to continue
35 ESP_LOGE(TAG, "Failed to allocate pointers");
36 return false;
37 }
38
39 // Initialize all pointers to null
40 for (size_t i = 0; i < needed_buffers; i++) {
41 temp_buffers[i] = nullptr;
42 }
43
44 // Try to allocate all the buffers
45 bool allocation_success = true;
46 for (size_t i = 0; i < needed_buffers; i++) {
47 size_t this_buffer_size = current_buffer_size;
48 // Last buffer might be smaller if total_length is not divisible by current_buffer_size
49 if (i == needed_buffers - 1 && total_length % current_buffer_size != 0) {
50 this_buffer_size = total_length % current_buffer_size;
51 }
52
53 temp_buffers[i] = allocator.allocate(this_buffer_size);
54 if (temp_buffers[i] == nullptr) {
55 allocation_success = false;
56 break;
57 }
58
59 // Initialize buffer to zero
60 memset(temp_buffers[i], 0, this_buffer_size);
61 }
62
63 if (allocation_success) {
64 // Success! Store the result
65 this->buffers_ = temp_buffers;
66 this->buffer_count_ = needed_buffers;
67 this->buffer_size_ = current_buffer_size;
68 this->total_length_ = total_length;
69 ESP_LOGD(TAG, "Allocated %zu * %zu bytes - %zu bytes", this->buffer_count_, this->buffer_size_,
70 this->total_length_);
71 return true;
72 }
73
74 // Allocation failed, clean up and try smaller buffers
75 for (size_t i = 0; i < needed_buffers; i++) {
76 if (temp_buffers[i] != nullptr) {
77 allocator.deallocate(temp_buffers[i], 0);
78 }
79 }
80 ptr_allocator.deallocate(temp_buffers, 0);
81
82 // Halve the buffer size and try again
83 current_buffer_size = current_buffer_size / 2;
84 }
85
86 ESP_LOGE(TAG, "Failed to allocate %zu bytes", total_length);
87 return false;
88}
89
91 if (this->buffers_ != nullptr) {
92 RAMAllocator<uint8_t> allocator;
93 for (size_t i = 0; i < this->buffer_count_; i++) {
94 if (this->buffers_[i] != nullptr) {
95 allocator.deallocate(this->buffers_[i], 0);
96 }
97 }
98 RAMAllocator<uint8_t *> ptr_allocator;
99 ptr_allocator.deallocate(this->buffers_, 0);
100 this->buffers_ = nullptr;
101 }
102 this->buffer_count_ = 0;
103 this->buffer_size_ = 0;
104 this->total_length_ = 0;
105}
106
107const uint8_t &SplitBuffer::operator[](size_t index) const {
108 if (index >= this->total_length_) {
109 ESP_LOGE(TAG, "Out of bounds - %zu >= %zu", index, this->total_length_);
110 // Return reference to a static dummy byte since we can't throw exceptions.
111 // the byte is non-const since it will also be used by the non-const [] overload.
112 static uint8_t dummy = 0;
113 return dummy;
114 }
115
116 const auto buffer_index = index / this->buffer_size_;
117 const auto offset_in_buffer = index % this->buffer_size_;
118
119 return this->buffers_[buffer_index][offset_in_buffer];
120}
121
122// non-const version of operator[] for write access
123uint8_t &SplitBuffer::operator[](size_t index) {
124 // avoid code duplication. These casts are safe since we know the object is not const.
125 return const_cast<uint8_t &>(static_cast<const SplitBuffer *>(this)->operator[](index));
126}
127
128const uint8_t *SplitBuffer::get_span(size_t index, size_t &length) const {
129 if (index >= this->total_length_) {
130 length = 0;
131 return nullptr;
132 }
133 const size_t offset = index % this->buffer_size_;
134 length = std::min(this->buffer_size_ - offset, this->total_length_ - index);
135 return this->buffers_[index / this->buffer_size_] + offset;
136}
137
138uint8_t *SplitBuffer::get_span(size_t index, size_t &length) {
139 return const_cast<uint8_t *>(static_cast<const SplitBuffer *>(this)->get_span(index, length));
140}
141
142void SplitBuffer::write(size_t index, const uint8_t *data, size_t length) {
143 while (length != 0) {
144 size_t span_length;
145 uint8_t *span = this->get_span(index, span_length);
146 if (span == nullptr)
147 return;
148 span_length = std::min(span_length, length);
149 memcpy(span, data, span_length);
150 index += span_length;
151 data += span_length;
152 length -= span_length;
153 }
154}
155
160void SplitBuffer::fill(uint8_t value) const {
161 if (this->buffer_count_ == 0)
162 return;
163 // clear all the full sized buffers
164 size_t i = 0;
165 for (; i != this->buffer_count_ - 1; i++) {
166 memset(this->buffers_[i], value, this->buffer_size_);
167 }
168 // clear the last, potentially short, buffer.
169 // `i` is guaranteed to equal the last index since the loop terminates at that value.
170 // where all buffers are the same size, the modulus must return the size, not 0.
171 auto size_last = ((this->total_length_ - 1) % this->buffer_size_) + 1;
172 memset(this->buffers_[i], value, size_last);
173}
174
175} // namespace esphome::split_buffer
An STL allocator that uses SPI or internal RAM.
Definition helpers.h:2298
void deallocate(T *p, size_t n)
Definition helpers.h:2355
T * allocate(size_t n)
Definition helpers.h:2325
A SplitBuffer allocates a large memory buffer potentially as multiple smaller buffers to facilitate a...
bool init(size_t total_length, size_t max_buffer_size=SIZE_MAX)
void fill(uint8_t value) const
Fill the entire buffer with a single byte value.
void write(size_t index, const uint8_t *data, size_t length)
const uint8_t * get_span(size_t index, size_t &length) const
uint8_t & operator[](size_t index)
ESPHOME_LOG_TAG(TAG, "split_buffer")
uint16_t length
Definition tt21100.cpp:0