ESPHome  2024.11.0
bytebuffer.h
Go to the documentation of this file.
1 #pragma once
2 
3 #include <utility>
4 #include <vector>
5 #include <cinttypes>
6 #include <cstddef>
7 #include "esphome/core/helpers.h"
8 
9 namespace esphome {
10 namespace bytebuffer {
11 
12 enum Endian { LITTLE, BIG };
13 
38 class ByteBuffer {
39  public:
40  // Default constructor (compatibility with TEMPLATABLE_VALUE)
41  // Creates a zero-length ByteBuffer which is little use to anybody.
42  ByteBuffer() : ByteBuffer(std::vector<uint8_t>()) {}
43 
47  ByteBuffer(size_t capacity, Endian endianness = LITTLE)
48  : data_(std::vector<uint8_t>(capacity)), endianness_(endianness), limit_(capacity){};
49 
50  // templated functions to implement putting and getting data of various types. There are two flavours of all
51  // functions - one that uses the position as the offset, and updates the position accordingly, and one that
52  // takes an explicit offset and does not update the position.
53  // Separate temnplates are provided for types that fit into 32 bits and those that are bigger. These delegate
54  // the actual put/get to common code based around those sizes.
55  // This reduces the code size and execution time for smaller types. A similar structure for e.g. 16 bits is unlikely
56  // to provide any further benefit given that all target platforms are native 32 bit.
57 
58  template<typename T>
59  T get(typename std::enable_if<std::is_integral<T>::value, T>::type * = 0,
61  // integral types that fit into 32 bit
62  return static_cast<T>(this->get_uint32_(sizeof(T)));
63  }
64 
65  template<typename T>
66  T get(size_t offset, typename std::enable_if<std::is_integral<T>::value, T>::type * = 0,
68  return static_cast<T>(this->get_uint32_(offset, sizeof(T)));
69  }
70 
71  template<typename T>
72  void put(const T &value, typename std::enable_if<std::is_integral<T>::value, T>::type * = 0,
73  typename std::enable_if<(sizeof(T) <= sizeof(uint32_t)), T>::type * = 0) {
74  this->put_uint32_(static_cast<uint32_t>(value), sizeof(T));
75  }
76 
77  template<typename T>
78  void put(const T &value, size_t offset, typename std::enable_if<std::is_integral<T>::value, T>::type * = 0,
79  typename std::enable_if<(sizeof(T) <= sizeof(uint32_t)), T>::type * = 0) {
80  this->put_uint32_(static_cast<uint32_t>(value), offset, sizeof(T));
81  }
82 
83  // integral types that do not fit into 32 bit (basically only 64 bit types)
84  template<typename T>
85  T get(typename std::enable_if<std::is_integral<T>::value, T>::type * = 0,
87  return static_cast<T>(this->get_uint64_(sizeof(T)));
88  }
89 
90  template<typename T>
91  T get(size_t offset, typename std::enable_if<std::is_integral<T>::value, T>::type * = 0,
93  return static_cast<T>(this->get_uint64_(offset, sizeof(T)));
94  }
95 
96  template<typename T>
97  void put(const T &value, typename std::enable_if<std::is_integral<T>::value, T>::type * = 0,
98  typename std::enable_if<(sizeof(T) == sizeof(uint64_t)), T>::type * = 0) {
99  this->put_uint64_(value, sizeof(T));
100  }
101 
102  template<typename T>
103  void put(const T &value, size_t offset, typename std::enable_if<std::is_integral<T>::value, T>::type * = 0,
104  typename std::enable_if<(sizeof(T) == sizeof(uint64_t)), T>::type * = 0) {
105  this->put_uint64_(static_cast<uint64_t>(value), offset, sizeof(T));
106  }
107 
108  // floating point types. Caters for 32 and 64 bit floating point.
109  template<typename T>
110  T get(typename std::enable_if<std::is_floating_point<T>::value, T>::type * = 0,
112  return bit_cast<T>(this->get_uint32_(sizeof(T)));
113  }
114 
115  template<typename T>
116  T get(typename std::enable_if<std::is_floating_point<T>::value, T>::type * = 0,
118  return bit_cast<T>(this->get_uint64_(sizeof(T)));
119  }
120 
121  template<typename T>
122  T get(size_t offset, typename std::enable_if<std::is_floating_point<T>::value, T>::type * = 0,
124  return bit_cast<T>(this->get_uint32_(offset, sizeof(T)));
125  }
126 
127  template<typename T>
128  T get(size_t offset, typename std::enable_if<std::is_floating_point<T>::value, T>::type * = 0,
130  return bit_cast<T>(this->get_uint64_(offset, sizeof(T)));
131  }
132  template<typename T>
133  void put(const T &value, typename std::enable_if<std::is_floating_point<T>::value, T>::type * = 0,
134  typename std::enable_if<(sizeof(T) <= sizeof(uint32_t)), T>::type * = 0) {
135  this->put_uint32_(bit_cast<uint32_t>(value), sizeof(T));
136  }
137 
138  template<typename T>
139  void put(const T &value, typename std::enable_if<std::is_floating_point<T>::value, T>::type * = 0,
140  typename std::enable_if<(sizeof(T) == sizeof(uint64_t)), T>::type * = 0) {
141  this->put_uint64_(bit_cast<uint64_t>(value), sizeof(T));
142  }
143 
144  template<typename T>
145  void put(const T &value, size_t offset, typename std::enable_if<std::is_floating_point<T>::value, T>::type * = 0,
146  typename std::enable_if<(sizeof(T) <= sizeof(uint32_t)), T>::type * = 0) {
147  this->put_uint32_(bit_cast<uint32_t>(value), offset, sizeof(T));
148  }
149 
150  template<typename T>
151  void put(const T &value, size_t offset, typename std::enable_if<std::is_floating_point<T>::value, T>::type * = 0,
152  typename std::enable_if<(sizeof(T) == sizeof(uint64_t)), T>::type * = 0) {
153  this->put_uint64_(bit_cast<uint64_t>(value), offset, sizeof(T));
154  }
155 
156  template<typename T> static ByteBuffer wrap(T value, Endian endianness = LITTLE) {
157  ByteBuffer buffer = ByteBuffer(sizeof(T), endianness);
158  buffer.put(value);
159  buffer.flip();
160  return buffer;
161  }
162 
163  static ByteBuffer wrap(std::vector<uint8_t> const &data, Endian endianness = LITTLE) {
164  ByteBuffer buffer = {data};
165  buffer.endianness_ = endianness;
166  return buffer;
167  }
168 
169  static ByteBuffer wrap(const uint8_t *ptr, size_t len, Endian endianness = LITTLE) {
170  return wrap(std::vector<uint8_t>(ptr, ptr + len), endianness);
171  }
172 
173  // convenience functions with explicit types named..
174  void put_float(float value) { this->put(value); }
175  void put_double(double value) { this->put(value); }
176 
177  uint8_t get_uint8() { return this->data_[this->position_++]; }
178  // Get a 16 bit unsigned value, increment by 2
179  uint16_t get_uint16() { return this->get<uint16_t>(); }
180  // Get a 24 bit unsigned value, increment by 3
181  uint32_t get_uint24() { return this->get_uint32_(3); };
182  // Get a 32 bit unsigned value, increment by 4
183  uint32_t get_uint32() { return this->get<uint32_t>(); };
184  // Get a 64 bit unsigned value, increment by 8
185  uint64_t get_uint64() { return this->get<uint64_t>(); };
186  // Signed versions of the get functions
187  uint8_t get_int8() { return static_cast<int8_t>(this->get_uint8()); };
188  int16_t get_int16() { return this->get<uint16_t>(); }
189  int32_t get_int32() { return this->get<int32_t>(); }
190  int64_t get_int64() { return this->get<int64_t>(); }
191  // Get a float value, increment by 4
192  float get_float() { return this->get<float>(); }
193  // Get a double value, increment by 8
194  double get_double() { return this->get<double>(); }
195 
196  // Get a bool value, increment by 1
197  bool get_bool() { return static_cast<bool>(this->get_uint8()); }
198 
199  uint32_t get_int24(size_t offset) {
200  auto value = this->get_uint24(offset);
201  uint32_t mask = (~static_cast<uint32_t>(0)) << 23;
202  if ((value & mask) != 0)
203  value |= mask;
204  return value;
205  }
206 
207  uint32_t get_int24() {
208  auto value = this->get_uint24();
209  uint32_t mask = (~static_cast<uint32_t>(0)) << 23;
210  if ((value & mask) != 0)
211  value |= mask;
212  return value;
213  }
214  std::vector<uint8_t> get_vector(size_t length, size_t offset) {
215  auto start = this->data_.begin() + offset;
216  return {start, start + length};
217  }
218 
219  std::vector<uint8_t> get_vector(size_t length) {
220  auto result = this->get_vector(length, this->position_);
221  this->position_ += length;
222  return result;
223  }
224 
225  // Convenience named functions
226  void put_uint8(uint8_t value) { this->data_[this->position_++] = value; }
227  void put_uint16(uint16_t value) { this->put(value); }
228  void put_uint24(uint32_t value) { this->put_uint32_(value, 3); }
229  void put_uint32(uint32_t value) { this->put(value); }
230  void put_uint64(uint64_t value) { this->put(value); }
231  // Signed versions of the put functions
232  void put_int8(int8_t value) { this->put_uint8(static_cast<uint8_t>(value)); }
233  void put_int16(int16_t value) { this->put(value); }
234  void put_int24(int32_t value) { this->put_uint32_(value, 3); }
235  void put_int32(int32_t value) { this->put(value); }
236  void put_int64(int64_t value) { this->put(value); }
237  // Extra put functions
238  void put_bool(bool value) { this->put_uint8(value); }
239 
240  // versions of the above with an offset, these do not update the position
241 
242  uint64_t get_uint64(size_t offset) { return this->get<uint64_t>(offset); }
243  uint32_t get_uint24(size_t offset) { return this->get_uint32_(offset, 3); };
244  double get_double(size_t offset) { return get<double>(offset); }
245 
246  // Get one byte from the buffer, increment position by 1
247  uint8_t get_uint8(size_t offset) { return this->data_[offset]; }
248  // Get a 16 bit unsigned value, increment by 2
249  uint16_t get_uint16(size_t offset) { return get<uint16_t>(offset); }
250  // Get a 24 bit unsigned value, increment by 3
251  uint32_t get_uint32(size_t offset) { return this->get<uint32_t>(offset); };
252  // Get a 64 bit unsigned value, increment by 8
253  uint8_t get_int8(size_t offset) { return get<int8_t>(offset); }
254  int16_t get_int16(size_t offset) { return get<int16_t>(offset); }
255  int32_t get_int32(size_t offset) { return get<int32_t>(offset); }
256  int64_t get_int64(size_t offset) { return get<int64_t>(offset); }
257  // Get a float value, increment by 4
258  float get_float(size_t offset) { return get<float>(offset); }
259  // Get a double value, increment by 8
260 
261  // Get a bool value, increment by 1
262  bool get_bool(size_t offset) { return this->get_uint8(offset); }
263 
264  void put_uint8(uint8_t value, size_t offset) { this->data_[offset] = value; }
265  void put_uint16(uint16_t value, size_t offset) { this->put(value, offset); }
266  void put_uint24(uint32_t value, size_t offset) { this->put(value, offset); }
267  void put_uint32(uint32_t value, size_t offset) { this->put(value, offset); }
268  void put_uint64(uint64_t value, size_t offset) { this->put(value, offset); }
269  // Signed versions of the put functions
270  void put_int8(int8_t value, size_t offset) { this->put_uint8(static_cast<uint8_t>(value), offset); }
271  void put_int16(int16_t value, size_t offset) { this->put(value, offset); }
272  void put_int24(int32_t value, size_t offset) { this->put_uint32_(value, offset, 3); }
273  void put_int32(int32_t value, size_t offset) { this->put(value, offset); }
274  void put_int64(int64_t value, size_t offset) { this->put(value, offset); }
275  // Extra put functions
276  void put_float(float value, size_t offset) { this->put(value, offset); }
277  void put_double(double value, size_t offset) { this->put(value, offset); }
278  void put_bool(bool value, size_t offset) { this->put_uint8(value, offset); }
279  void put(const std::vector<uint8_t> &value, size_t offset) {
280  std::copy(value.begin(), value.end(), this->data_.begin() + offset);
281  }
282  void put_vector(const std::vector<uint8_t> &value, size_t offset) { this->put(value, offset); }
283  void put(const std::vector<uint8_t> &value) {
284  this->put_vector(value, this->position_);
285  this->position_ += value.size();
286  }
287  void put_vector(const std::vector<uint8_t> &value) { this->put(value); }
288 
289  // Getters
290 
291  inline size_t get_capacity() const { return this->data_.size(); }
292  inline size_t get_position() const { return this->position_; }
293  inline size_t get_limit() const { return this->limit_; }
294  inline size_t get_remaining() const { return this->get_limit() - this->get_position(); }
295  inline Endian get_endianness() const { return this->endianness_; }
296  inline void mark() { this->mark_ = this->position_; }
297  inline void big_endian() { this->endianness_ = BIG; }
298  inline void little_endian() { this->endianness_ = LITTLE; }
299  // retrieve a pointer to the underlying data.
300  std::vector<uint8_t> get_data() { return this->data_; };
301 
302  void get_bytes(void *dest, size_t length) {
303  std::copy(this->data_.begin() + this->position_, this->data_.begin() + this->position_ + length, (uint8_t *) dest);
304  this->position_ += length;
305  }
306 
307  void get_bytes(void *dest, size_t length, size_t offset) {
308  std::copy(this->data_.begin() + offset, this->data_.begin() + offset + length, (uint8_t *) dest);
309  }
310 
311  void rewind() { this->position_ = 0; }
312  void reset() { this->position_ = this->mark_; }
313 
314  void set_limit(size_t limit) { this->limit_ = limit; }
315  void set_position(size_t position) { this->position_ = position; }
316  void clear() {
317  this->limit_ = this->get_capacity();
318  this->position_ = 0;
319  }
320  void flip() {
321  this->limit_ = this->position_;
322  this->position_ = 0;
323  }
324 
325  protected:
326  uint64_t get_uint64_(size_t offset, size_t length) const {
327  uint64_t value = 0;
328  if (this->endianness_ == LITTLE) {
329  offset += length;
330  while (length-- != 0) {
331  value <<= 8;
332  value |= this->data_[--offset];
333  }
334  } else {
335  while (length-- != 0) {
336  value <<= 8;
337  value |= this->data_[offset++];
338  }
339  }
340  return value;
341  }
342 
343  uint64_t get_uint64_(size_t length) {
344  auto result = this->get_uint64_(this->position_, length);
345  this->position_ += length;
346  return result;
347  }
348  uint32_t get_uint32_(size_t offset, size_t length) const {
349  uint32_t value = 0;
350  if (this->endianness_ == LITTLE) {
351  offset += length;
352  while (length-- != 0) {
353  value <<= 8;
354  value |= this->data_[--offset];
355  }
356  } else {
357  while (length-- != 0) {
358  value <<= 8;
359  value |= this->data_[offset++];
360  }
361  }
362  return value;
363  }
364 
365  uint32_t get_uint32_(size_t length) {
366  auto result = this->get_uint32_(this->position_, length);
367  this->position_ += length;
368  return result;
369  }
370 
372 
373  void put_uint64_(uint64_t value, size_t length) {
374  this->put_uint64_(value, this->position_, length);
375  this->position_ += length;
376  }
377  void put_uint32_(uint32_t value, size_t length) {
378  this->put_uint32_(value, this->position_, length);
379  this->position_ += length;
380  }
381 
382  void put_uint64_(uint64_t value, size_t offset, size_t length) {
383  if (this->endianness_ == LITTLE) {
384  while (length-- != 0) {
385  this->data_[offset++] = static_cast<uint8_t>(value);
386  value >>= 8;
387  }
388  } else {
389  offset += length;
390  while (length-- != 0) {
391  this->data_[--offset] = static_cast<uint8_t>(value);
392  value >>= 8;
393  }
394  }
395  }
396 
397  void put_uint32_(uint32_t value, size_t offset, size_t length) {
398  if (this->endianness_ == LITTLE) {
399  while (length-- != 0) {
400  this->data_[offset++] = static_cast<uint8_t>(value);
401  value >>= 8;
402  }
403  } else {
404  offset += length;
405  while (length-- != 0) {
406  this->data_[--offset] = static_cast<uint8_t>(value);
407  value >>= 8;
408  }
409  }
410  }
411  ByteBuffer(std::vector<uint8_t> const &data) : data_(data), limit_(data.size()) {}
412 
413  std::vector<uint8_t> data_;
415  size_t position_{0};
416  size_t mark_{0};
417  size_t limit_{0};
418 };
419 
420 } // namespace bytebuffer
421 } // namespace esphome
void put_int8(int8_t value, size_t offset)
Definition: bytebuffer.h:270
void put(const T &value, size_t offset, typename std::enable_if< std::is_floating_point< T >::value, T >::type *=0, typename std::enable_if<(sizeof(T)<=sizeof(uint32_t)), T >::type *=0)
Definition: bytebuffer.h:145
ByteBuffer(size_t capacity, Endian endianness=LITTLE)
Create a new Bytebuffer with the given capacity.
Definition: bytebuffer.h:47
std::vector< uint8_t > get_vector(size_t length, size_t offset)
Definition: bytebuffer.h:214
void put_uint32_(uint32_t value, size_t length)
Definition: bytebuffer.h:377
void put(const T &value, typename std::enable_if< std::is_floating_point< T >::value, T >::type *=0, typename std::enable_if<(sizeof(T)==sizeof(uint64_t)), T >::type *=0)
Definition: bytebuffer.h:139
static ByteBuffer wrap(std::vector< uint8_t > const &data, Endian endianness=LITTLE)
Definition: bytebuffer.h:163
uint32_t get_int24(size_t offset)
Definition: bytebuffer.h:199
void put_uint64(uint64_t value)
Definition: bytebuffer.h:230
void put_int64(int64_t value)
Definition: bytebuffer.h:236
void put(const T &value, typename std::enable_if< std::is_integral< T >::value, T >::type *=0, typename std::enable_if<(sizeof(T)==sizeof(uint64_t)), T >::type *=0)
Definition: bytebuffer.h:97
void put_vector(const std::vector< uint8_t > &value, size_t offset)
Definition: bytebuffer.h:282
void put_int8(int8_t value)
Definition: bytebuffer.h:232
uint32_t get_uint32_(size_t offset, size_t length) const
Definition: bytebuffer.h:348
void put_int16(int16_t value, size_t offset)
Definition: bytebuffer.h:271
void put_float(float value)
Definition: bytebuffer.h:174
void set_position(size_t position)
Definition: bytebuffer.h:315
void put_uint16(uint16_t value, size_t offset)
Definition: bytebuffer.h:265
STL namespace.
void get_bytes(void *dest, size_t length)
Definition: bytebuffer.h:302
void put_uint8(uint8_t value, size_t offset)
Definition: bytebuffer.h:264
int32_t get_int32(size_t offset)
Definition: bytebuffer.h:255
int16_t get_int16(size_t offset)
Definition: bytebuffer.h:254
std::vector< uint8_t > get_data()
Definition: bytebuffer.h:300
bool get_bool(size_t offset)
Definition: bytebuffer.h:262
uint32_t get_uint32_(size_t length)
Definition: bytebuffer.h:365
ByteBuffer(std::vector< uint8_t > const &data)
Definition: bytebuffer.h:411
uint64_t get_uint64(size_t offset)
Definition: bytebuffer.h:242
int64_t get_int64(size_t offset)
Definition: bytebuffer.h:256
void put(const std::vector< uint8_t > &value)
Definition: bytebuffer.h:283
void put_int24(int32_t value, size_t offset)
Definition: bytebuffer.h:272
void put_vector(const std::vector< uint8_t > &value)
Definition: bytebuffer.h:287
void put(const T &value, typename std::enable_if< std::is_floating_point< T >::value, T >::type *=0, typename std::enable_if<(sizeof(T)<=sizeof(uint32_t)), T >::type *=0)
Definition: bytebuffer.h:133
A class modelled on the Java ByteBuffer class.
Definition: bytebuffer.h:38
void put_int64(int64_t value, size_t offset)
Definition: bytebuffer.h:274
void put_uint32(uint32_t value, size_t offset)
Definition: bytebuffer.h:267
void put_double(double value, size_t offset)
Definition: bytebuffer.h:277
uint32_t get_uint32(size_t offset)
Definition: bytebuffer.h:251
uint32_t get_uint24(size_t offset)
Definition: bytebuffer.h:243
void put_uint16(uint16_t value)
Definition: bytebuffer.h:227
double get_double(size_t offset)
Definition: bytebuffer.h:244
uint8_t type
void put_uint32_(uint32_t value, size_t offset, size_t length)
Definition: bytebuffer.h:397
void put_uint24(uint32_t value, size_t offset)
Definition: bytebuffer.h:266
void put_int32(int32_t value)
Definition: bytebuffer.h:235
void put(const T &value, size_t offset, typename std::enable_if< std::is_integral< T >::value, T >::type *=0, typename std::enable_if<(sizeof(T)==sizeof(uint64_t)), T >::type *=0)
Definition: bytebuffer.h:103
void put(const std::vector< uint8_t > &value, size_t offset)
Definition: bytebuffer.h:279
uint8_t get_int8(size_t offset)
Definition: bytebuffer.h:253
void put_uint8(uint8_t value)
Definition: bytebuffer.h:226
void put_uint64(uint64_t value, size_t offset)
Definition: bytebuffer.h:268
void put_uint24(uint32_t value)
Definition: bytebuffer.h:228
void get_bytes(void *dest, size_t length, size_t offset)
Definition: bytebuffer.h:307
void put_double(double value)
Definition: bytebuffer.h:175
std::string size_t len
Definition: helpers.h:293
void put_int24(int32_t value)
Definition: bytebuffer.h:234
float get_float(size_t offset)
Definition: bytebuffer.h:258
uint16_t get_uint16(size_t offset)
Definition: bytebuffer.h:249
To bit_cast(const From &src)
Convert data between types, without aliasing issues or undefined behaviour.
Definition: helpers.h:122
void put_uint64_(uint64_t value, size_t length)
Putters.
Definition: bytebuffer.h:373
uint16_t length
Definition: tt21100.cpp:12
void put_bool(bool value, size_t offset)
Definition: bytebuffer.h:278
void put_int16(int16_t value)
Definition: bytebuffer.h:233
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
void put_uint32(uint32_t value)
Definition: bytebuffer.h:229
uint64_t get_uint64_(size_t length)
Definition: bytebuffer.h:343
void put_int32(int32_t value, size_t offset)
Definition: bytebuffer.h:273
static ByteBuffer wrap(const uint8_t *ptr, size_t len, Endian endianness=LITTLE)
Definition: bytebuffer.h:169
float position
Definition: cover.h:14
void put_float(float value, size_t offset)
Definition: bytebuffer.h:276
uint8_t get_uint8(size_t offset)
Definition: bytebuffer.h:247
void put(const T &value, size_t offset, typename std::enable_if< std::is_integral< T >::value, T >::type *=0, typename std::enable_if<(sizeof(T)<=sizeof(uint32_t)), T >::type *=0)
Definition: bytebuffer.h:78
void put_uint64_(uint64_t value, size_t offset, size_t length)
Definition: bytebuffer.h:382
static ByteBuffer wrap(T value, Endian endianness=LITTLE)
Definition: bytebuffer.h:156
std::vector< uint8_t > data_
Definition: bytebuffer.h:413
uint64_t get_uint64_(size_t offset, size_t length) const
Definition: bytebuffer.h:326
void put(const T &value, typename std::enable_if< std::is_integral< T >::value, T >::type *=0, typename std::enable_if<(sizeof(T)<=sizeof(uint32_t)), T >::type *=0)
Definition: bytebuffer.h:72
void set_limit(size_t limit)
Definition: bytebuffer.h:314
void put(const T &value, size_t offset, typename std::enable_if< std::is_floating_point< T >::value, T >::type *=0, typename std::enable_if<(sizeof(T)==sizeof(uint64_t)), T >::type *=0)
Definition: bytebuffer.h:151
std::vector< uint8_t > get_vector(size_t length)
Definition: bytebuffer.h:219