mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	feat: Expand ByteBuffer (#7316)
Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com> Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
		| @@ -1,19 +1,64 @@ | ||||
| #include "bytebuffer.h" | ||||
| #include <cassert> | ||||
| #include <cstring> | ||||
|  | ||||
| namespace esphome { | ||||
|  | ||||
| ByteBuffer ByteBuffer::create(size_t capacity) { | ||||
|   std::vector<uint8_t> data(capacity); | ||||
|   return {data}; | ||||
| } | ||||
|  | ||||
| ByteBuffer ByteBuffer::wrap(uint8_t *ptr, size_t len) { | ||||
| ByteBuffer ByteBuffer::wrap(const uint8_t *ptr, size_t len, Endian endianness) { | ||||
|   // there is a double copy happening here, could be optimized but at cost of clarity. | ||||
|   std::vector<uint8_t> data(ptr, ptr + len); | ||||
|   return {data}; | ||||
|   ByteBuffer buffer = {data}; | ||||
|   buffer.endianness_ = endianness; | ||||
|   return buffer; | ||||
| } | ||||
|  | ||||
| ByteBuffer ByteBuffer::wrap(std::vector<uint8_t> data) { return {std::move(data)}; } | ||||
| ByteBuffer ByteBuffer::wrap(std::vector<uint8_t> const &data, Endian endianness) { | ||||
|   ByteBuffer buffer = {data}; | ||||
|   buffer.endianness_ = endianness; | ||||
|   return buffer; | ||||
| } | ||||
|  | ||||
| ByteBuffer ByteBuffer::wrap(uint8_t value) { | ||||
|   ByteBuffer buffer = ByteBuffer(1); | ||||
|   buffer.put_uint8(value); | ||||
|   buffer.flip(); | ||||
|   return buffer; | ||||
| } | ||||
|  | ||||
| ByteBuffer ByteBuffer::wrap(uint16_t value, Endian endianness) { | ||||
|   ByteBuffer buffer = ByteBuffer(2, endianness); | ||||
|   buffer.put_uint16(value); | ||||
|   buffer.flip(); | ||||
|   return buffer; | ||||
| } | ||||
|  | ||||
| ByteBuffer ByteBuffer::wrap(uint32_t value, Endian endianness) { | ||||
|   ByteBuffer buffer = ByteBuffer(4, endianness); | ||||
|   buffer.put_uint32(value); | ||||
|   buffer.flip(); | ||||
|   return buffer; | ||||
| } | ||||
|  | ||||
| ByteBuffer ByteBuffer::wrap(uint64_t value, Endian endianness) { | ||||
|   ByteBuffer buffer = ByteBuffer(8, endianness); | ||||
|   buffer.put_uint64(value); | ||||
|   buffer.flip(); | ||||
|   return buffer; | ||||
| } | ||||
|  | ||||
| ByteBuffer ByteBuffer::wrap(float value, Endian endianness) { | ||||
|   ByteBuffer buffer = ByteBuffer(sizeof(float), endianness); | ||||
|   buffer.put_float(value); | ||||
|   buffer.flip(); | ||||
|   return buffer; | ||||
| } | ||||
|  | ||||
| ByteBuffer ByteBuffer::wrap(double value, Endian endianness) { | ||||
|   ByteBuffer buffer = ByteBuffer(sizeof(double), endianness); | ||||
|   buffer.put_double(value); | ||||
|   buffer.flip(); | ||||
|   return buffer; | ||||
| } | ||||
|  | ||||
| void ByteBuffer::set_limit(size_t limit) { | ||||
|   assert(limit <= this->get_capacity()); | ||||
| @@ -27,108 +72,102 @@ void ByteBuffer::clear() { | ||||
|   this->limit_ = this->get_capacity(); | ||||
|   this->position_ = 0; | ||||
| } | ||||
| uint16_t ByteBuffer::get_uint16() { | ||||
|   assert(this->get_remaining() >= 2); | ||||
|   uint16_t value; | ||||
|   if (endianness_ == LITTLE) { | ||||
|     value = this->data_[this->position_++]; | ||||
|     value |= this->data_[this->position_++] << 8; | ||||
|   } else { | ||||
|     value = this->data_[this->position_++] << 8; | ||||
|     value |= this->data_[this->position_++]; | ||||
|   } | ||||
|   return value; | ||||
| void ByteBuffer::flip() { | ||||
|   this->limit_ = this->position_; | ||||
|   this->position_ = 0; | ||||
| } | ||||
|  | ||||
| uint32_t ByteBuffer::get_uint32() { | ||||
|   assert(this->get_remaining() >= 4); | ||||
|   uint32_t value; | ||||
|   if (endianness_ == LITTLE) { | ||||
|     value = this->data_[this->position_++]; | ||||
|     value |= this->data_[this->position_++] << 8; | ||||
|     value |= this->data_[this->position_++] << 16; | ||||
|     value |= this->data_[this->position_++] << 24; | ||||
|   } else { | ||||
|     value = this->data_[this->position_++] << 24; | ||||
|     value |= this->data_[this->position_++] << 16; | ||||
|     value |= this->data_[this->position_++] << 8; | ||||
|     value |= this->data_[this->position_++]; | ||||
|   } | ||||
|   return value; | ||||
| } | ||||
| uint32_t ByteBuffer::get_uint24() { | ||||
|   assert(this->get_remaining() >= 3); | ||||
|   uint32_t value; | ||||
|   if (endianness_ == LITTLE) { | ||||
|     value = this->data_[this->position_++]; | ||||
|     value |= this->data_[this->position_++] << 8; | ||||
|     value |= this->data_[this->position_++] << 16; | ||||
|   } else { | ||||
|     value = this->data_[this->position_++] << 16; | ||||
|     value |= this->data_[this->position_++] << 8; | ||||
|     value |= this->data_[this->position_++]; | ||||
|   } | ||||
|   return value; | ||||
| } | ||||
| uint32_t ByteBuffer::get_int24() { | ||||
|   auto value = this->get_uint24(); | ||||
|   uint32_t mask = (~(uint32_t) 0) << 23; | ||||
|   if ((value & mask) != 0) | ||||
|     value |= mask; | ||||
|   return value; | ||||
| } | ||||
| /// Getters | ||||
| uint8_t ByteBuffer::get_uint8() { | ||||
|   assert(this->get_remaining() >= 1); | ||||
|   return this->data_[this->position_++]; | ||||
| } | ||||
| float ByteBuffer::get_float() { | ||||
|   auto value = this->get_uint32(); | ||||
|   return *(float *) &value; | ||||
| uint64_t ByteBuffer::get_uint(size_t length) { | ||||
|   assert(this->get_remaining() >= length); | ||||
|   uint64_t value = 0; | ||||
|   if (this->endianness_ == LITTLE) { | ||||
|     this->position_ += length; | ||||
|     auto index = this->position_; | ||||
|     while (length-- != 0) { | ||||
|       value <<= 8; | ||||
|       value |= this->data_[--index]; | ||||
|     } | ||||
|   } else { | ||||
|     while (length-- != 0) { | ||||
|       value <<= 8; | ||||
|       value |= this->data_[this->position_++]; | ||||
|     } | ||||
|   } | ||||
|   return value; | ||||
| } | ||||
|  | ||||
| uint32_t ByteBuffer::get_int24() { | ||||
|   auto value = this->get_uint24(); | ||||
|   uint32_t mask = (~static_cast<uint32_t>(0)) << 23; | ||||
|   if ((value & mask) != 0) | ||||
|     value |= mask; | ||||
|   return value; | ||||
| } | ||||
| float ByteBuffer::get_float() { | ||||
|   assert(this->get_remaining() >= sizeof(float)); | ||||
|   auto ui_value = this->get_uint32(); | ||||
|   float value; | ||||
|   memcpy(&value, &ui_value, sizeof(float)); | ||||
|   return value; | ||||
| } | ||||
| double ByteBuffer::get_double() { | ||||
|   assert(this->get_remaining() >= sizeof(double)); | ||||
|   auto ui_value = this->get_uint64(); | ||||
|   double value; | ||||
|   memcpy(&value, &ui_value, sizeof(double)); | ||||
|   return value; | ||||
| } | ||||
| std::vector<uint8_t> ByteBuffer::get_vector(size_t length) { | ||||
|   assert(this->get_remaining() >= length); | ||||
|   auto start = this->data_.begin() + this->position_; | ||||
|   this->position_ += length; | ||||
|   return {start, start + length}; | ||||
| } | ||||
|  | ||||
| /// Putters | ||||
| void ByteBuffer::put_uint8(uint8_t value) { | ||||
|   assert(this->get_remaining() >= 1); | ||||
|   this->data_[this->position_++] = value; | ||||
| } | ||||
|  | ||||
| void ByteBuffer::put_uint16(uint16_t value) { | ||||
|   assert(this->get_remaining() >= 2); | ||||
| void ByteBuffer::put_uint(uint64_t value, size_t length) { | ||||
|   assert(this->get_remaining() >= length); | ||||
|   if (this->endianness_ == LITTLE) { | ||||
|     this->data_[this->position_++] = (uint8_t) value; | ||||
|     this->data_[this->position_++] = (uint8_t) (value >> 8); | ||||
|     while (length-- != 0) { | ||||
|       this->data_[this->position_++] = static_cast<uint8_t>(value); | ||||
|       value >>= 8; | ||||
|     } | ||||
|   } else { | ||||
|     this->data_[this->position_++] = (uint8_t) (value >> 8); | ||||
|     this->data_[this->position_++] = (uint8_t) value; | ||||
|     this->position_ += length; | ||||
|     auto index = this->position_; | ||||
|     while (length-- != 0) { | ||||
|       this->data_[--index] = static_cast<uint8_t>(value); | ||||
|       value >>= 8; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| void ByteBuffer::put_uint24(uint32_t value) { | ||||
|   assert(this->get_remaining() >= 3); | ||||
|   if (this->endianness_ == LITTLE) { | ||||
|     this->data_[this->position_++] = (uint8_t) value; | ||||
|     this->data_[this->position_++] = (uint8_t) (value >> 8); | ||||
|     this->data_[this->position_++] = (uint8_t) (value >> 16); | ||||
|   } else { | ||||
|     this->data_[this->position_++] = (uint8_t) (value >> 16); | ||||
|     this->data_[this->position_++] = (uint8_t) (value >> 8); | ||||
|     this->data_[this->position_++] = (uint8_t) value; | ||||
|   } | ||||
| void ByteBuffer::put_float(float value) { | ||||
|   static_assert(sizeof(float) == sizeof(uint32_t), "Float sizes other than 32 bit not supported"); | ||||
|   assert(this->get_remaining() >= sizeof(float)); | ||||
|   uint32_t ui_value; | ||||
|   memcpy(&ui_value, &value, sizeof(float));  // this work-around required to silence compiler warnings | ||||
|   this->put_uint32(ui_value); | ||||
| } | ||||
| void ByteBuffer::put_uint32(uint32_t value) { | ||||
|   assert(this->get_remaining() >= 4); | ||||
|   if (this->endianness_ == LITTLE) { | ||||
|     this->data_[this->position_++] = (uint8_t) value; | ||||
|     this->data_[this->position_++] = (uint8_t) (value >> 8); | ||||
|     this->data_[this->position_++] = (uint8_t) (value >> 16); | ||||
|     this->data_[this->position_++] = (uint8_t) (value >> 24); | ||||
|   } else { | ||||
|     this->data_[this->position_++] = (uint8_t) (value >> 24); | ||||
|     this->data_[this->position_++] = (uint8_t) (value >> 16); | ||||
|     this->data_[this->position_++] = (uint8_t) (value >> 8); | ||||
|     this->data_[this->position_++] = (uint8_t) value; | ||||
|   } | ||||
| void ByteBuffer::put_double(double value) { | ||||
|   static_assert(sizeof(double) == sizeof(uint64_t), "Double sizes other than 64 bit not supported"); | ||||
|   assert(this->get_remaining() >= sizeof(double)); | ||||
|   uint64_t ui_value; | ||||
|   memcpy(&ui_value, &value, sizeof(double)); | ||||
|   this->put_uint64(ui_value); | ||||
| } | ||||
| void ByteBuffer::put_float(float value) { this->put_uint32(*(uint32_t *) &value); } | ||||
| void ByteBuffer::flip() { | ||||
|   this->limit_ = this->position_; | ||||
|   this->position_ = 0; | ||||
| void ByteBuffer::put_vector(const std::vector<uint8_t> &value) { | ||||
|   assert(this->get_remaining() >= value.size()); | ||||
|   std::copy(value.begin(), value.end(), this->data_.begin() + this->position_); | ||||
|   this->position_ += value.size(); | ||||
| } | ||||
| }  // namespace esphome | ||||
|   | ||||
| @@ -15,55 +15,103 @@ enum Endian { LITTLE, BIG }; | ||||
|  * | ||||
|  * There are three variables maintained pointing into the buffer: | ||||
|  * | ||||
|  * 0 <= position <= limit <= capacity | ||||
|  * | ||||
|  * capacity: the maximum amount of data that can be stored | ||||
|  * capacity: the maximum amount of data that can be stored - set on construction and cannot be changed | ||||
|  * limit: the limit of the data currently available to get or put | ||||
|  * position: the current insert or extract position | ||||
|  * | ||||
|  * 0 <= position <= limit <= capacity | ||||
|  * | ||||
|  * In addition a mark can be set to the current position with mark(). A subsequent call to reset() will restore | ||||
|  * the position to the mark. | ||||
|  * | ||||
|  * The buffer can be marked to be little-endian (default) or big-endian. All subsequent operations will use that order. | ||||
|  * | ||||
|  * The flip() operation will reset the position to 0 and limit to the current position. This is useful for reading | ||||
|  * data from a buffer after it has been written. | ||||
|  * | ||||
|  */ | ||||
| class ByteBuffer { | ||||
|  public: | ||||
|   // Default constructor (compatibility with TEMPLATABLE_VALUE) | ||||
|   ByteBuffer() : ByteBuffer(std::vector<uint8_t>()) {} | ||||
|   /** | ||||
|    * Create a new Bytebuffer with the given capacity | ||||
|    */ | ||||
|   static ByteBuffer create(size_t capacity); | ||||
|   ByteBuffer(size_t capacity, Endian endianness = LITTLE) | ||||
|       : data_(std::vector<uint8_t>(capacity)), endianness_(endianness), limit_(capacity){}; | ||||
|   /** | ||||
|    * Wrap an existing vector in a Bytebufffer | ||||
|    * Wrap an existing vector in a ByteBufffer | ||||
|    */ | ||||
|   static ByteBuffer wrap(std::vector<uint8_t> data); | ||||
|   static ByteBuffer wrap(std::vector<uint8_t> const &data, Endian endianness = LITTLE); | ||||
|   /** | ||||
|    * Wrap an existing array in a Bytebufffer | ||||
|    * Wrap an existing array in a ByteBuffer. Note that this will create a copy of the data. | ||||
|    */ | ||||
|   static ByteBuffer wrap(uint8_t *ptr, size_t len); | ||||
|   static ByteBuffer wrap(const uint8_t *ptr, size_t len, Endian endianness = LITTLE); | ||||
|   // Convenience functions to create a ByteBuffer from a value | ||||
|   static ByteBuffer wrap(uint8_t value); | ||||
|   static ByteBuffer wrap(uint16_t value, Endian endianness = LITTLE); | ||||
|   static ByteBuffer wrap(uint32_t value, Endian endianness = LITTLE); | ||||
|   static ByteBuffer wrap(uint64_t value, Endian endianness = LITTLE); | ||||
|   static ByteBuffer wrap(int8_t value) { return wrap(static_cast<uint8_t>(value)); } | ||||
|   static ByteBuffer wrap(int16_t value, Endian endianness = LITTLE) { | ||||
|     return wrap(static_cast<uint16_t>(value), endianness); | ||||
|   } | ||||
|   static ByteBuffer wrap(int32_t value, Endian endianness = LITTLE) { | ||||
|     return wrap(static_cast<uint32_t>(value), endianness); | ||||
|   } | ||||
|   static ByteBuffer wrap(int64_t value, Endian endianness = LITTLE) { | ||||
|     return wrap(static_cast<uint64_t>(value), endianness); | ||||
|   } | ||||
|   static ByteBuffer wrap(float value, Endian endianness = LITTLE); | ||||
|   static ByteBuffer wrap(double value, Endian endianness = LITTLE); | ||||
|   static ByteBuffer wrap(bool value) { return wrap(static_cast<uint8_t>(value)); } | ||||
|  | ||||
|   // Get an integral value from the buffer, increment position by length | ||||
|   uint64_t get_uint(size_t length); | ||||
|   // Get one byte from the buffer, increment position by 1 | ||||
|   uint8_t get_uint8(); | ||||
|   // Get a 16 bit unsigned value, increment by 2 | ||||
|   uint16_t get_uint16(); | ||||
|   uint16_t get_uint16() { return static_cast<uint16_t>(this->get_uint(sizeof(uint16_t))); }; | ||||
|   // Get a 24 bit unsigned value, increment by 3 | ||||
|   uint32_t get_uint24(); | ||||
|   uint32_t get_uint24() { return static_cast<uint32_t>(this->get_uint(3)); }; | ||||
|   // Get a 32 bit unsigned value, increment by 4 | ||||
|   uint32_t get_uint32(); | ||||
|   // signed versions of the get functions | ||||
|   uint8_t get_int8() { return (int8_t) this->get_uint8(); }; | ||||
|   int16_t get_int16() { return (int16_t) this->get_uint16(); } | ||||
|   uint32_t get_uint32() { return static_cast<uint32_t>(this->get_uint(sizeof(uint32_t))); }; | ||||
|   // Get a 64 bit unsigned value, increment by 8 | ||||
|   uint64_t get_uint64() { return this->get_uint(sizeof(uint64_t)); }; | ||||
|   // Signed versions of the get functions | ||||
|   uint8_t get_int8() { return static_cast<int8_t>(this->get_uint8()); }; | ||||
|   int16_t get_int16() { return static_cast<int16_t>(this->get_uint(sizeof(int16_t))); } | ||||
|   uint32_t get_int24(); | ||||
|   int32_t get_int32() { return (int32_t) this->get_uint32(); } | ||||
|   int32_t get_int32() { return static_cast<int32_t>(this->get_uint(sizeof(int32_t))); } | ||||
|   int64_t get_int64() { return static_cast<int64_t>(this->get_uint(sizeof(int64_t))); } | ||||
|   // Get a float value, increment by 4 | ||||
|   float get_float(); | ||||
|   // Get a double value, increment by 8 | ||||
|   double get_double(); | ||||
|   // Get a bool value, increment by 1 | ||||
|   bool get_bool() { return this->get_uint8(); } | ||||
|   // Get vector of bytes, increment by length | ||||
|   std::vector<uint8_t> get_vector(size_t length); | ||||
|  | ||||
|   // put values into the buffer, increment the position accordingly | ||||
|   // Put values into the buffer, increment the position accordingly | ||||
|   // put any integral value, length represents the number of bytes | ||||
|   void put_uint(uint64_t value, size_t length); | ||||
|   void put_uint8(uint8_t value); | ||||
|   void put_uint16(uint16_t value); | ||||
|   void put_uint24(uint32_t value); | ||||
|   void put_uint32(uint32_t value); | ||||
|   void put_uint16(uint16_t value) { this->put_uint(value, sizeof(uint16_t)); } | ||||
|   void put_uint24(uint32_t value) { this->put_uint(value, 3); } | ||||
|   void put_uint32(uint32_t value) { this->put_uint(value, sizeof(uint32_t)); } | ||||
|   void put_uint64(uint64_t value) { this->put_uint(value, sizeof(uint64_t)); } | ||||
|   // Signed versions of the put functions | ||||
|   void put_int8(int8_t value) { this->put_uint8(static_cast<uint8_t>(value)); } | ||||
|   void put_int16(int32_t value) { this->put_uint(static_cast<uint16_t>(value), sizeof(uint16_t)); } | ||||
|   void put_int24(int32_t value) { this->put_uint(static_cast<uint32_t>(value), 3); } | ||||
|   void put_int32(int32_t value) { this->put_uint(static_cast<uint32_t>(value), sizeof(uint32_t)); } | ||||
|   void put_int64(int64_t value) { this->put_uint(static_cast<uint64_t>(value), sizeof(uint64_t)); } | ||||
|   // Extra put functions | ||||
|   void put_float(float value); | ||||
|   void put_double(double value); | ||||
|   void put_bool(bool value) { this->put_uint8(value); } | ||||
|   void put_vector(const std::vector<uint8_t> &value); | ||||
|  | ||||
|   inline size_t get_capacity() const { return this->data_.size(); } | ||||
|   inline size_t get_position() const { return this->position_; } | ||||
| @@ -80,12 +128,12 @@ class ByteBuffer { | ||||
|   // set limit to current position, postition to zero. Used when swapping from write to read operations. | ||||
|   void flip(); | ||||
|   // retrieve a pointer to the underlying data. | ||||
|   uint8_t *array() { return this->data_.data(); }; | ||||
|   std::vector<uint8_t> get_data() { return this->data_; }; | ||||
|   void rewind() { this->position_ = 0; } | ||||
|   void reset() { this->position_ = this->mark_; } | ||||
|  | ||||
|  protected: | ||||
|   ByteBuffer(std::vector<uint8_t> data) : data_(std::move(data)) { this->limit_ = this->get_capacity(); } | ||||
|   ByteBuffer(std::vector<uint8_t> const &data) : data_(data), limit_(data.size()) {} | ||||
|   std::vector<uint8_t> data_; | ||||
|   Endian endianness_{LITTLE}; | ||||
|   size_t position_{0}; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user