mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 15:12:06 +00:00 
			
		
		
		
	preen
This commit is contained in:
		| @@ -61,9 +61,19 @@ static_assert(offsetof(esp_ble_gap_cb_param_t, read_rssi_cmpl.rssi) == sizeof(es | |||||||
| static_assert(offsetof(esp_ble_gap_cb_param_t, read_rssi_cmpl.remote_addr) == sizeof(esp_bt_status_t) + sizeof(int8_t), | static_assert(offsetof(esp_ble_gap_cb_param_t, read_rssi_cmpl.remote_addr) == sizeof(esp_bt_status_t) + sizeof(int8_t), | ||||||
|               "remote_addr must follow rssi in read_rssi_cmpl"); |               "remote_addr must follow rssi in read_rssi_cmpl"); | ||||||
|  |  | ||||||
| // Maximum size for inline storage of GATTC/GATTS data | // Param struct sizes on ESP32 | ||||||
| // This value is chosen to fit within the 80-byte union while maximizing inline storage | static constexpr size_t GATTC_PARAM_SIZE = 28; | ||||||
| static constexpr size_t INLINE_DATA_SIZE = 68; | static constexpr size_t GATTS_PARAM_SIZE = 32; | ||||||
|  |  | ||||||
|  | // Maximum size for inline storage of data | ||||||
|  | // GATTC: 80 - 28 (param) - 8 (other fields) = 44 bytes for data | ||||||
|  | // GATTS: 80 - 32 (param) - 8 (other fields) = 40 bytes for data | ||||||
|  | static constexpr size_t GATTC_INLINE_DATA_SIZE = 44; | ||||||
|  | static constexpr size_t GATTS_INLINE_DATA_SIZE = 40; | ||||||
|  |  | ||||||
|  | // Verify param struct sizes | ||||||
|  | static_assert(sizeof(esp_ble_gattc_cb_param_t) == GATTC_PARAM_SIZE, "GATTC param size unexpected"); | ||||||
|  | static_assert(sizeof(esp_ble_gatts_cb_param_t) == GATTS_PARAM_SIZE, "GATTS param size unexpected"); | ||||||
|  |  | ||||||
| // Received GAP, GATTC and GATTS events are only queued, and get processed in the main loop(). | // Received GAP, GATTC and GATTS events are only queued, and get processed in the main loop(). | ||||||
| // This class stores each event with minimal memory usage. | // This class stores each event with minimal memory usage. | ||||||
| @@ -115,21 +125,21 @@ class BLEEvent { | |||||||
|     this->init_gap_data_(e, p); |     this->init_gap_data_(e, p); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   // Constructor for GATTC events - uses heap allocation |   // Constructor for GATTC events - param stored inline, data may use heap | ||||||
|   // IMPORTANT: The heap allocation is REQUIRED and must not be removed as an optimization. |   // IMPORTANT: We MUST copy the param struct because the pointer from ESP-IDF | ||||||
|   // The param pointer from ESP-IDF is only valid during the callback execution. |   // is only valid during the callback execution. Since BLE events are processed | ||||||
|   // Since BLE events are processed asynchronously in the main loop, we must create |   // asynchronously in the main loop, we store our own copy inline to ensure | ||||||
|   // our own copy to ensure the data remains valid until the event is processed. |   // the data remains valid until the event is processed. | ||||||
|   BLEEvent(esp_gattc_cb_event_t e, esp_gatt_if_t i, esp_ble_gattc_cb_param_t *p) { |   BLEEvent(esp_gattc_cb_event_t e, esp_gatt_if_t i, esp_ble_gattc_cb_param_t *p) { | ||||||
|     this->type_ = GATTC; |     this->type_ = GATTC; | ||||||
|     this->init_gattc_data_(e, i, p); |     this->init_gattc_data_(e, i, p); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   // Constructor for GATTS events - uses heap allocation |   // Constructor for GATTS events - param stored inline, data may use heap | ||||||
|   // IMPORTANT: The heap allocation is REQUIRED and must not be removed as an optimization. |   // IMPORTANT: We MUST copy the param struct because the pointer from ESP-IDF | ||||||
|   // The param pointer from ESP-IDF is only valid during the callback execution. |   // is only valid during the callback execution. Since BLE events are processed | ||||||
|   // Since BLE events are processed asynchronously in the main loop, we must create |   // asynchronously in the main loop, we store our own copy inline to ensure | ||||||
|   // our own copy to ensure the data remains valid until the event is processed. |   // the data remains valid until the event is processed. | ||||||
|   BLEEvent(esp_gatts_cb_event_t e, esp_gatt_if_t i, esp_ble_gatts_cb_param_t *p) { |   BLEEvent(esp_gatts_cb_event_t e, esp_gatt_if_t i, esp_ble_gatts_cb_param_t *p) { | ||||||
|     this->type_ = GATTS; |     this->type_ = GATTS; | ||||||
|     this->init_gatts_data_(e, i, p); |     this->init_gatts_data_(e, i, p); | ||||||
| @@ -148,24 +158,20 @@ class BLEEvent { | |||||||
|         // GAP events don't have heap allocations |         // GAP events don't have heap allocations | ||||||
|         break; |         break; | ||||||
|       case GATTC: |       case GATTC: | ||||||
|         delete this->event_.gattc.gattc_param; |         // Param is now stored inline, only delete heap data if it was heap-allocated | ||||||
|         // Only delete heap data if it was heap-allocated |  | ||||||
|         if (!this->event_.gattc.is_inline && this->event_.gattc.data.heap_data != nullptr) { |         if (!this->event_.gattc.is_inline && this->event_.gattc.data.heap_data != nullptr) { | ||||||
|           delete[] this->event_.gattc.data.heap_data; |           delete[] this->event_.gattc.data.heap_data; | ||||||
|         } |         } | ||||||
|         // Clear critical fields to prevent issues if type changes |         // Clear critical fields to prevent issues if type changes | ||||||
|         this->event_.gattc.gattc_param = nullptr; |  | ||||||
|         this->event_.gattc.is_inline = false; |         this->event_.gattc.is_inline = false; | ||||||
|         this->event_.gattc.data.heap_data = nullptr; |         this->event_.gattc.data.heap_data = nullptr; | ||||||
|         break; |         break; | ||||||
|       case GATTS: |       case GATTS: | ||||||
|         delete this->event_.gatts.gatts_param; |         // Param is now stored inline, only delete heap data if it was heap-allocated | ||||||
|         // Only delete heap data if it was heap-allocated |  | ||||||
|         if (!this->event_.gatts.is_inline && this->event_.gatts.data.heap_data != nullptr) { |         if (!this->event_.gatts.is_inline && this->event_.gatts.data.heap_data != nullptr) { | ||||||
|           delete[] this->event_.gatts.data.heap_data; |           delete[] this->event_.gatts.data.heap_data; | ||||||
|         } |         } | ||||||
|         // Clear critical fields to prevent issues if type changes |         // Clear critical fields to prevent issues if type changes | ||||||
|         this->event_.gatts.gatts_param = nullptr; |  | ||||||
|         this->event_.gatts.is_inline = false; |         this->event_.gatts.is_inline = false; | ||||||
|         this->event_.gatts.data.heap_data = nullptr; |         this->event_.gatts.data.heap_data = nullptr; | ||||||
|         break; |         break; | ||||||
| @@ -220,12 +226,12 @@ class BLEEvent { | |||||||
|  |  | ||||||
|     // NOLINTNEXTLINE(readability-identifier-naming) |     // NOLINTNEXTLINE(readability-identifier-naming) | ||||||
|     struct gattc_event { |     struct gattc_event { | ||||||
|       esp_ble_gattc_cb_param_t *gattc_param;  // Heap-allocated (4 bytes) |       esp_ble_gattc_cb_param_t gattc_param;  // Stored inline (28 bytes) | ||||||
|       esp_gattc_cb_event_t gattc_event;      // 4 bytes |       esp_gattc_cb_event_t gattc_event;      // 4 bytes | ||||||
|       union { |       union { | ||||||
|         uint8_t *heap_data;                           // 4 bytes when heap-allocated |         uint8_t *heap_data;                           // 4 bytes when heap-allocated | ||||||
|         uint8_t inline_data[INLINE_DATA_SIZE];  // INLINE_DATA_SIZE bytes when stored inline |         uint8_t inline_data[GATTC_INLINE_DATA_SIZE];  // 44 bytes when stored inline | ||||||
|       } data;                                   // INLINE_DATA_SIZE bytes total |       } data;                                         // 44 bytes total | ||||||
|       uint16_t data_len;                              // 2 bytes |       uint16_t data_len;                              // 2 bytes | ||||||
|       esp_gatt_if_t gattc_if;                         // 1 byte |       esp_gatt_if_t gattc_if;                         // 1 byte | ||||||
|       bool is_inline;                                 // 1 byte - true when data is stored inline |       bool is_inline;                                 // 1 byte - true when data is stored inline | ||||||
| @@ -233,12 +239,12 @@ class BLEEvent { | |||||||
|  |  | ||||||
|     // NOLINTNEXTLINE(readability-identifier-naming) |     // NOLINTNEXTLINE(readability-identifier-naming) | ||||||
|     struct gatts_event { |     struct gatts_event { | ||||||
|       esp_ble_gatts_cb_param_t *gatts_param;  // Heap-allocated (4 bytes) |       esp_ble_gatts_cb_param_t gatts_param;  // Stored inline (32 bytes) | ||||||
|       esp_gatts_cb_event_t gatts_event;      // 4 bytes |       esp_gatts_cb_event_t gatts_event;      // 4 bytes | ||||||
|       union { |       union { | ||||||
|         uint8_t *heap_data;                           // 4 bytes when heap-allocated |         uint8_t *heap_data;                           // 4 bytes when heap-allocated | ||||||
|         uint8_t inline_data[INLINE_DATA_SIZE];  // INLINE_DATA_SIZE bytes when stored inline |         uint8_t inline_data[GATTS_INLINE_DATA_SIZE];  // 40 bytes when stored inline | ||||||
|       } data;                                   // INLINE_DATA_SIZE bytes total |       } data;                                         // 40 bytes total | ||||||
|       uint16_t data_len;                              // 2 bytes |       uint16_t data_len;                              // 2 bytes | ||||||
|       esp_gatt_if_t gatts_if;                         // 1 byte |       esp_gatt_if_t gatts_if;                         // 1 byte | ||||||
|       bool is_inline;                                 // 1 byte - true when data is stored inline |       bool is_inline;                                 // 1 byte - true when data is stored inline | ||||||
| @@ -258,12 +264,12 @@ class BLEEvent { | |||||||
|  |  | ||||||
|  private: |  private: | ||||||
|   // Helper to copy data with inline storage optimization |   // Helper to copy data with inline storage optimization | ||||||
|   template<typename EventStruct> |   template<typename EventStruct, size_t InlineSize> | ||||||
|   void copy_data_with_inline_storage_(EventStruct &event, const uint8_t *src_data, uint16_t len, |   void copy_data_with_inline_storage_(EventStruct &event, const uint8_t *src_data, uint16_t len, | ||||||
|                                       uint8_t **param_value_ptr) { |                                       uint8_t **param_value_ptr) { | ||||||
|     event.data_len = len; |     event.data_len = len; | ||||||
|     if (len > 0) { |     if (len > 0) { | ||||||
|       if (len <= INLINE_DATA_SIZE) { |       if (len <= InlineSize) { | ||||||
|         event.is_inline = true; |         event.is_inline = true; | ||||||
|         memcpy(event.data.inline_data, src_data, len); |         memcpy(event.data.inline_data, src_data, len); | ||||||
|         *param_value_ptr = event.data.inline_data; |         *param_value_ptr = event.data.inline_data; | ||||||
| @@ -364,34 +370,33 @@ class BLEEvent { | |||||||
|     this->event_.gattc.gattc_if = i; |     this->event_.gattc.gattc_if = i; | ||||||
|  |  | ||||||
|     if (p == nullptr) { |     if (p == nullptr) { | ||||||
|       this->event_.gattc.gattc_param = nullptr; |       // Zero out the param struct when null | ||||||
|  |       memset(&this->event_.gattc.gattc_param, 0, sizeof(this->event_.gattc.gattc_param)); | ||||||
|       this->event_.gattc.is_inline = false; |       this->event_.gattc.is_inline = false; | ||||||
|       this->event_.gattc.data.heap_data = nullptr; |       this->event_.gattc.data.heap_data = nullptr; | ||||||
|       this->event_.gattc.data_len = 0; |       this->event_.gattc.data_len = 0; | ||||||
|       return;  // Invalid event, but we can't log in header file |       return;  // Invalid event, but we can't log in header file | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // Heap-allocate param |     // Copy param struct inline (no heap allocation!) | ||||||
|     // Heap allocation is used because GATTC/GATTS events are rare (<1% of events) |     // GATTC/GATTS events are rare (<1% of events) but we can still store them inline | ||||||
|     // while GAP events (99%) are stored inline to minimize memory usage |     // along with small data payloads, eliminating all heap allocations for typical BLE operations | ||||||
|     // IMPORTANT: This heap allocation provides clear ownership semantics: |     // CRITICAL: This copy is REQUIRED for memory safety - the ESP-IDF param pointer | ||||||
|     // - The BLEEvent owns the allocated memory for its lifetime |     // is only valid during the callback and will be reused/freed after we return | ||||||
|     // - The data remains valid from the BLE callback context until processed in the main loop |     this->event_.gattc.gattc_param = *p; | ||||||
|     // - Without this copy, we'd have use-after-free bugs as ESP-IDF reuses the callback memory |  | ||||||
|     this->event_.gattc.gattc_param = new esp_ble_gattc_cb_param_t(*p); |  | ||||||
|  |  | ||||||
|     // Copy data for events that need it |     // Copy data for events that need it | ||||||
|     // The param struct contains pointers (e.g., notify.value) that point to temporary buffers. |     // The param struct contains pointers (e.g., notify.value) that point to temporary buffers. | ||||||
|     // We must copy this data to ensure it remains valid when the event is processed later. |     // We must copy this data to ensure it remains valid when the event is processed later. | ||||||
|     switch (e) { |     switch (e) { | ||||||
|       case ESP_GATTC_NOTIFY_EVT: |       case ESP_GATTC_NOTIFY_EVT: | ||||||
|         copy_data_with_inline_storage_(this->event_.gattc, p->notify.value, p->notify.value_len, |         copy_data_with_inline_storage_<decltype(this->event_.gattc), GATTC_INLINE_DATA_SIZE>( | ||||||
|                                        &this->event_.gattc.gattc_param->notify.value); |             this->event_.gattc, p->notify.value, p->notify.value_len, &this->event_.gattc.gattc_param.notify.value); | ||||||
|         break; |         break; | ||||||
|       case ESP_GATTC_READ_CHAR_EVT: |       case ESP_GATTC_READ_CHAR_EVT: | ||||||
|       case ESP_GATTC_READ_DESCR_EVT: |       case ESP_GATTC_READ_DESCR_EVT: | ||||||
|         copy_data_with_inline_storage_(this->event_.gattc, p->read.value, p->read.value_len, |         copy_data_with_inline_storage_<decltype(this->event_.gattc), GATTC_INLINE_DATA_SIZE>( | ||||||
|                                        &this->event_.gattc.gattc_param->read.value); |             this->event_.gattc, p->read.value, p->read.value_len, &this->event_.gattc.gattc_param.read.value); | ||||||
|         break; |         break; | ||||||
|       default: |       default: | ||||||
|         this->event_.gattc.is_inline = false; |         this->event_.gattc.is_inline = false; | ||||||
| @@ -407,29 +412,28 @@ class BLEEvent { | |||||||
|     this->event_.gatts.gatts_if = i; |     this->event_.gatts.gatts_if = i; | ||||||
|  |  | ||||||
|     if (p == nullptr) { |     if (p == nullptr) { | ||||||
|       this->event_.gatts.gatts_param = nullptr; |       // Zero out the param struct when null | ||||||
|  |       memset(&this->event_.gatts.gatts_param, 0, sizeof(this->event_.gatts.gatts_param)); | ||||||
|       this->event_.gatts.is_inline = false; |       this->event_.gatts.is_inline = false; | ||||||
|       this->event_.gatts.data.heap_data = nullptr; |       this->event_.gatts.data.heap_data = nullptr; | ||||||
|       this->event_.gatts.data_len = 0; |       this->event_.gatts.data_len = 0; | ||||||
|       return;  // Invalid event, but we can't log in header file |       return;  // Invalid event, but we can't log in header file | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // Heap-allocate param |     // Copy param struct inline (no heap allocation!) | ||||||
|     // Heap allocation is used because GATTC/GATTS events are rare (<1% of events) |     // GATTC/GATTS events are rare (<1% of events) but we can still store them inline | ||||||
|     // while GAP events (99%) are stored inline to minimize memory usage |     // along with small data payloads, eliminating all heap allocations for typical BLE operations | ||||||
|     // IMPORTANT: This heap allocation provides clear ownership semantics: |     // CRITICAL: This copy is REQUIRED for memory safety - the ESP-IDF param pointer | ||||||
|     // - The BLEEvent owns the allocated memory for its lifetime |     // is only valid during the callback and will be reused/freed after we return | ||||||
|     // - The data remains valid from the BLE callback context until processed in the main loop |     this->event_.gatts.gatts_param = *p; | ||||||
|     // - Without this copy, we'd have use-after-free bugs as ESP-IDF reuses the callback memory |  | ||||||
|     this->event_.gatts.gatts_param = new esp_ble_gatts_cb_param_t(*p); |  | ||||||
|  |  | ||||||
|     // Copy data for events that need it |     // Copy data for events that need it | ||||||
|     // The param struct contains pointers (e.g., write.value) that point to temporary buffers. |     // The param struct contains pointers (e.g., write.value) that point to temporary buffers. | ||||||
|     // We must copy this data to ensure it remains valid when the event is processed later. |     // We must copy this data to ensure it remains valid when the event is processed later. | ||||||
|     switch (e) { |     switch (e) { | ||||||
|       case ESP_GATTS_WRITE_EVT: |       case ESP_GATTS_WRITE_EVT: | ||||||
|         copy_data_with_inline_storage_(this->event_.gatts, p->write.value, p->write.len, |         copy_data_with_inline_storage_<decltype(this->event_.gatts), GATTS_INLINE_DATA_SIZE>( | ||||||
|                                        &this->event_.gatts.gatts_param->write.value); |             this->event_.gatts, p->write.value, p->write.len, &this->event_.gatts.gatts_param.write.value); | ||||||
|         break; |         break; | ||||||
|       default: |       default: | ||||||
|         this->event_.gatts.is_inline = false; |         this->event_.gatts.is_inline = false; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user