mirror of
https://github.com/esphome/esphome.git
synced 2025-10-18 01:33:51 +01:00
Merge branch 'fixed_vector_bluetooth_services' into integration
This commit is contained in:
@@ -1519,7 +1519,7 @@ message BluetoothGATTCharacteristic {
|
||||
repeated uint64 uuid = 1 [(fixed_array_size) = 2, (fixed_array_skip_zero) = true];
|
||||
uint32 handle = 2;
|
||||
uint32 properties = 3;
|
||||
repeated BluetoothGATTDescriptor descriptors = 4;
|
||||
repeated BluetoothGATTDescriptor descriptors = 4 [(fixed_vector) = true];
|
||||
|
||||
// New field for efficient UUID (v1.12+)
|
||||
// Only one of uuid or short_uuid will be set.
|
||||
@@ -1531,7 +1531,7 @@ message BluetoothGATTCharacteristic {
|
||||
message BluetoothGATTService {
|
||||
repeated uint64 uuid = 1 [(fixed_array_size) = 2, (fixed_array_skip_zero) = true];
|
||||
uint32 handle = 2;
|
||||
repeated BluetoothGATTCharacteristic characteristics = 3;
|
||||
repeated BluetoothGATTCharacteristic characteristics = 3 [(fixed_vector) = true];
|
||||
|
||||
// New field for efficient UUID (v1.12+)
|
||||
// Only one of uuid or short_uuid will be set.
|
||||
|
@@ -64,4 +64,10 @@ extend google.protobuf.FieldOptions {
|
||||
// This is typically done through methods returning const T& or special accessor
|
||||
// methods like get_options() or supported_modes_for_api_().
|
||||
optional string container_pointer = 50001;
|
||||
|
||||
// fixed_vector: Use FixedVector instead of std::vector for repeated fields
|
||||
// When set, the repeated field will use FixedVector<T> which requires calling
|
||||
// init(size) before adding elements. This eliminates std::vector template overhead
|
||||
// and is ideal when the exact size is known before populating the array.
|
||||
optional bool fixed_vector = 50013 [default=false];
|
||||
}
|
||||
|
@@ -1923,7 +1923,7 @@ class BluetoothGATTCharacteristic final : public ProtoMessage {
|
||||
std::array<uint64_t, 2> uuid{};
|
||||
uint32_t handle{0};
|
||||
uint32_t properties{0};
|
||||
std::vector<BluetoothGATTDescriptor> descriptors{};
|
||||
FixedVector<BluetoothGATTDescriptor> descriptors{};
|
||||
uint32_t short_uuid{0};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
@@ -1937,7 +1937,7 @@ class BluetoothGATTService final : public ProtoMessage {
|
||||
public:
|
||||
std::array<uint64_t, 2> uuid{};
|
||||
uint32_t handle{0};
|
||||
std::vector<BluetoothGATTCharacteristic> characteristics{};
|
||||
FixedVector<BluetoothGATTCharacteristic> characteristics{};
|
||||
uint32_t short_uuid{0};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
|
@@ -749,13 +749,29 @@ class ProtoSize {
|
||||
template<typename MessageType>
|
||||
inline void add_repeated_message(uint32_t field_id_size, const std::vector<MessageType> &messages) {
|
||||
// Skip if the vector is empty
|
||||
if (messages.empty()) {
|
||||
return;
|
||||
if (!messages.empty()) {
|
||||
// Use the force version for all messages in the repeated field
|
||||
for (const auto &message : messages) {
|
||||
add_message_object_force(field_id_size, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use the force version for all messages in the repeated field
|
||||
for (const auto &message : messages) {
|
||||
add_message_object_force(field_id_size, message);
|
||||
/**
|
||||
* @brief Calculates and adds the sizes of all messages in a repeated field to the total message size (FixedVector
|
||||
* version)
|
||||
*
|
||||
* @tparam MessageType The type of the nested messages in the FixedVector
|
||||
* @param messages FixedVector of message objects
|
||||
*/
|
||||
template<typename MessageType>
|
||||
inline void add_repeated_message(uint32_t field_id_size, const FixedVector<MessageType> &messages) {
|
||||
// Skip if the fixed vector is empty
|
||||
if (!messages.empty()) {
|
||||
// Use the force version for all messages in the repeated field
|
||||
for (const auto &message : messages) {
|
||||
add_message_object_force(field_id_size, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@@ -230,8 +230,8 @@ void BluetoothConnection::send_service_for_discovery_() {
|
||||
service_resp.handle = service_result.start_handle;
|
||||
|
||||
if (total_char_count > 0) {
|
||||
// Reserve space and process characteristics
|
||||
service_resp.characteristics.reserve(total_char_count);
|
||||
// Initialize FixedVector with exact count and process characteristics
|
||||
service_resp.characteristics.init(total_char_count);
|
||||
uint16_t char_offset = 0;
|
||||
esp_gattc_char_elem_t char_result;
|
||||
while (true) { // characteristics
|
||||
@@ -275,8 +275,8 @@ void BluetoothConnection::send_service_for_discovery_() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Reserve space and process descriptors
|
||||
characteristic_resp.descriptors.reserve(total_desc_count);
|
||||
// Initialize FixedVector with exact count and process descriptors
|
||||
characteristic_resp.descriptors.init(total_desc_count);
|
||||
uint16_t desc_offset = 0;
|
||||
esp_gattc_descr_elem_t desc_result;
|
||||
while (true) { // descriptors
|
||||
|
@@ -171,26 +171,67 @@ template<typename T> class FixedVector {
|
||||
size_t size_{0};
|
||||
size_t capacity_{0};
|
||||
|
||||
public:
|
||||
FixedVector() = default;
|
||||
|
||||
~FixedVector() {
|
||||
// Helper to destroy elements and free memory
|
||||
void cleanup_() {
|
||||
if (data_ != nullptr) {
|
||||
delete[] data_;
|
||||
// Manually destroy all elements
|
||||
for (size_t i = 0; i < size_; i++) {
|
||||
data_[i].~T();
|
||||
}
|
||||
// Free raw memory
|
||||
::operator delete(data_);
|
||||
}
|
||||
}
|
||||
|
||||
// Disable copy to avoid accidental copies
|
||||
FixedVector(const FixedVector &) = delete;
|
||||
FixedVector &operator=(const FixedVector &) = delete;
|
||||
// Helper to reset pointers after cleanup
|
||||
void reset_() {
|
||||
data_ = nullptr;
|
||||
capacity_ = 0;
|
||||
size_ = 0;
|
||||
}
|
||||
|
||||
// Allocate capacity - can only be called once on empty vector
|
||||
void init(size_t n) {
|
||||
if (data_ == nullptr && n > 0) {
|
||||
data_ = new T[n];
|
||||
capacity_ = n;
|
||||
size_ = 0;
|
||||
public:
|
||||
FixedVector() = default;
|
||||
|
||||
~FixedVector() { cleanup_(); }
|
||||
|
||||
// Enable move semantics for use in containers
|
||||
FixedVector(FixedVector &&other) noexcept : data_(other.data_), size_(other.size_), capacity_(other.capacity_) {
|
||||
other.reset_();
|
||||
}
|
||||
|
||||
FixedVector &operator=(FixedVector &&other) noexcept {
|
||||
if (this != &other) {
|
||||
// Delete our current data
|
||||
cleanup_();
|
||||
// Take ownership of other's data
|
||||
data_ = other.data_;
|
||||
size_ = other.size_;
|
||||
capacity_ = other.capacity_;
|
||||
// Leave other in valid empty state
|
||||
other.reset_();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Allocate capacity - can be called multiple times to reinit
|
||||
void init(size_t n) {
|
||||
cleanup_();
|
||||
reset_();
|
||||
if (n > 0) {
|
||||
// Allocate raw memory without calling constructors
|
||||
data_ = static_cast<T *>(::operator new(n * sizeof(T)));
|
||||
capacity_ = n;
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the vector (reset size to 0, keep capacity)
|
||||
void clear() { size_ = 0; }
|
||||
|
||||
// Shrink capacity to fit current size (frees all memory)
|
||||
void shrink_to_fit() {
|
||||
cleanup_();
|
||||
reset_();
|
||||
}
|
||||
|
||||
/// Add element without bounds checking
|
||||
@@ -198,16 +239,39 @@ template<typename T> class FixedVector {
|
||||
/// Silently ignores pushes beyond capacity (no exception or assertion)
|
||||
void push_back(const T &value) {
|
||||
if (size_ < capacity_) {
|
||||
data_[size_++] = value;
|
||||
// Use placement new to construct the object in pre-allocated memory
|
||||
new (&data_[size_]) T(value);
|
||||
size_++;
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct element in place and return reference
|
||||
/// Caller must ensure sufficient capacity was allocated via init()
|
||||
T &emplace_back() {
|
||||
if (size_ < capacity_) {
|
||||
return data_[size_++];
|
||||
}
|
||||
// Should never happen with proper init() - return last element to avoid crash
|
||||
return data_[capacity_ - 1];
|
||||
}
|
||||
|
||||
/// Access last element
|
||||
T &back() { return data_[size_ - 1]; }
|
||||
const T &back() const { return data_[size_ - 1]; }
|
||||
|
||||
size_t size() const { return size_; }
|
||||
bool empty() const { return size_ == 0; }
|
||||
|
||||
/// Access element without bounds checking (matches std::vector behavior)
|
||||
/// Caller must ensure index is valid (i < size())
|
||||
T &operator[](size_t i) { return data_[i]; }
|
||||
const T &operator[](size_t i) const { return data_[i]; }
|
||||
|
||||
// Iterator support for range-based for loops
|
||||
T *begin() { return data_; }
|
||||
T *end() { return data_ + size_; }
|
||||
const T *begin() const { return data_; }
|
||||
const T *end() const { return data_ + size_; }
|
||||
};
|
||||
|
||||
///@}
|
||||
|
@@ -12,7 +12,7 @@ platformio==6.1.18 # When updating platformio, also update /docker/Dockerfile
|
||||
esptool==5.1.0
|
||||
click==8.1.7
|
||||
esphome-dashboard==20251013.0
|
||||
aioesphomeapi==41.14.0
|
||||
aioesphomeapi==41.16.0
|
||||
zeroconf==0.148.0
|
||||
puremagic==1.30
|
||||
ruamel.yaml==0.18.15 # dashboard_import
|
||||
|
@@ -1,4 +1,4 @@
|
||||
pylint==3.3.9
|
||||
pylint==4.0.0
|
||||
flake8==7.3.0 # also change in .pre-commit-config.yaml when updating
|
||||
ruff==0.14.0 # also change in .pre-commit-config.yaml when updating
|
||||
pyupgrade==3.21.0 # also change in .pre-commit-config.yaml when updating
|
||||
|
@@ -1415,6 +1415,8 @@ class RepeatedTypeInfo(TypeInfo):
|
||||
# Check if this is a pointer field by looking for container_pointer option
|
||||
self._container_type = get_field_opt(field, pb.container_pointer, "")
|
||||
self._use_pointer = bool(self._container_type)
|
||||
# Check if this should use FixedVector instead of std::vector
|
||||
self._use_fixed_vector = get_field_opt(field, pb.fixed_vector, False)
|
||||
|
||||
# For repeated fields, we need to get the base type info
|
||||
# but we can't call create_field_type_info as it would cause recursion
|
||||
@@ -1438,6 +1440,8 @@ class RepeatedTypeInfo(TypeInfo):
|
||||
if "<" in self._container_type and ">" in self._container_type:
|
||||
return f"const {self._container_type}*"
|
||||
return f"const {self._container_type}<{self._ti.cpp_type}>*"
|
||||
if self._use_fixed_vector:
|
||||
return f"FixedVector<{self._ti.cpp_type}>"
|
||||
return f"std::vector<{self._ti.cpp_type}>"
|
||||
|
||||
@property
|
||||
|
Reference in New Issue
Block a user