From 9efe9f1c198b2d6039b4d5319afab53cad44d6be Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 29 Sep 2025 17:49:03 -0500 Subject: [PATCH] wip --- .../components/esp32_ble_server/__init__.py | 87 ++++++++++++++++ .../esp32_ble_server/ble_characteristic.cpp | 9 +- .../esp32_ble_server/ble_characteristic.h | 18 +++- .../esp32_ble_server/ble_descriptor.cpp | 4 +- .../esp32_ble_server/ble_descriptor.h | 14 ++- .../esp32_ble_server/ble_server.cpp | 4 +- .../components/esp32_ble_server/ble_server.h | 17 +++- .../ble_server_automations.cpp | 23 +++-- .../esp32_ble_server/ble_server_automations.h | 15 +-- .../esp32_ble_server/ble_service.cpp | 4 + .../components/esp32_ble_server/ble_service.h | 3 + .../esp32_improv/esp32_improv_component.cpp | 14 ++- .../components/event_emitter/event_emitter.h | 99 ++++++------------- 13 files changed, 197 insertions(+), 114 deletions(-) diff --git a/esphome/components/esp32_ble_server/__init__.py b/esphome/components/esp32_ble_server/__init__.py index 9eab9647b3..7bd3a0e585 100644 --- a/esphome/components/esp32_ble_server/__init__.py +++ b/esphome/components/esp32_ble_server/__init__.py @@ -1,4 +1,5 @@ import encodings +from typing import TypeAlias from esphome import automation import esphome.codegen as cg @@ -31,6 +32,92 @@ CODEOWNERS = ["@jesserockz", "@clydebarrow", "@Rapsssito"] DEPENDENCIES = ["esp32"] DOMAIN = "esp32_ble_server" +# Type aliases +_ListenerAllocation: TypeAlias = tuple[str, int | str, int] # (component, uuid, count) +_ServerListenerAllocation: TypeAlias = tuple[str, int] # (component, count) + +# Event listener allocation tracking - used by components to reserve slots +_LISTENER_ALLOCATIONS: dict[ + str, list[_ListenerAllocation | _ServerListenerAllocation] +] = { + "characteristic_write": [], + "characteristic_read": [], + "descriptor_write": [], + "server_connect": [], + "server_disconnect": [], +} + + +def allocate_characteristic_event_listener( + uuid: int | str, event_type: str, component: str, count: int = 1 +) -> None: + """ + Allocate event listener slots for a characteristic. + + Args: + uuid: The characteristic UUID (int or string) + event_type: "WRITE" or "READ" + component: Name of the component requesting allocation + count: Number of listeners needed (default 1) + """ + if event_type not in ("WRITE", "READ"): + raise ValueError(f"Unknown event_type: {event_type}") + + key = f"characteristic_{event_type.lower()}" + _LISTENER_ALLOCATIONS[key].append((component, uuid, count)) + + +def allocate_descriptor_event_listener( + uuid: int | str, event_type: str, component: str, count: int = 1 +) -> None: + """Allocate event listener slots for a descriptor.""" + if event_type != "WRITE": + raise ValueError(f"Unknown event_type: {event_type}") + + _LISTENER_ALLOCATIONS["descriptor_write"].append((component, uuid, count)) + + +def allocate_server_event_listener( + event_type: str, component: str, count: int = 1 +) -> None: + """Allocate event listener slots for server events.""" + if event_type not in ("CONNECT", "DISCONNECT"): + raise ValueError(f"Unknown event_type: {event_type}") + + key = f"server_{event_type.lower()}" + _LISTENER_ALLOCATIONS[key].append((component, count)) + + +def _sum_allocations_for_uuid(allocation_key: str, uuid: int | str) -> int: + """Helper to sum allocations for a specific UUID.""" + return sum( + count + for comp, alloc_uuid, count in _LISTENER_ALLOCATIONS[allocation_key] + if alloc_uuid == uuid + ) + + +def _get_allocations_for_uuid(uuid: int | str, event_type: str) -> int: + """Get total allocated listeners for a specific UUID and event type.""" + if event_type not in ("WRITE", "READ"): + return 0 + key = f"characteristic_{event_type.lower()}" + return _sum_allocations_for_uuid(key, uuid) + + +def _get_descriptor_allocations_for_uuid(uuid: int | str) -> int: + """Get total allocated listeners for a descriptor UUID.""" + return _sum_allocations_for_uuid("descriptor_write", uuid) + + +def sanitize_uuid_for_identifier(uuid: int | str) -> str: + """Convert UUID to valid C++ identifier.""" + if isinstance(uuid, int): + return f"0x{uuid:04X}" + # For string UUIDs, replace dashes and colons with underscores + return str(uuid).replace("-", "_").replace(":", "_").lower() + + CONF_ADVERTISE = "advertise" CONF_APPEARANCE = "appearance" CONF_BROADCAST = "broadcast" diff --git a/esphome/components/esp32_ble_server/ble_characteristic.cpp b/esphome/components/esp32_ble_server/ble_characteristic.cpp index fabcc75321..efa9c80b30 100644 --- a/esphome/components/esp32_ble_server/ble_characteristic.cpp +++ b/esphome/components/esp32_ble_server/ble_characteristic.cpp @@ -208,8 +208,7 @@ void BLECharacteristic::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt if (!param->read.need_rsp) break; // For some reason you can request a read but not want a response - this->EventEmitter::emit_(BLECharacteristicEvt::EmptyEvt::ON_READ, - param->read.conn_id); + this->emit_on_read_(param->read.conn_id); uint16_t max_offset = 22; @@ -277,8 +276,7 @@ void BLECharacteristic::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt } if (!param->write.is_prep) { - this->EventEmitter, uint16_t>::emit_( - BLECharacteristicEvt::VectorEvt::ON_WRITE, this->value_, param->write.conn_id); + this->emit_on_write_(this->value_, param->write.conn_id); } break; @@ -289,8 +287,7 @@ void BLECharacteristic::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt break; this->write_event_ = false; if (param->exec_write.exec_write_flag == ESP_GATT_PREP_WRITE_EXEC) { - this->EventEmitter, uint16_t>::emit_( - BLECharacteristicEvt::VectorEvt::ON_WRITE, this->value_, param->exec_write.conn_id); + this->emit_on_write_(this->value_, param->exec_write.conn_id); } esp_err_t err = esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, ESP_GATT_OK, nullptr); diff --git a/esphome/components/esp32_ble_server/ble_characteristic.h b/esphome/components/esp32_ble_server/ble_characteristic.h index 97b3af2a21..6b322bc7ff 100644 --- a/esphome/components/esp32_ble_server/ble_characteristic.h +++ b/esphome/components/esp32_ble_server/ble_characteristic.h @@ -6,6 +6,7 @@ #include "esphome/components/bytebuffer/bytebuffer.h" #include +#include #ifdef USE_ESP32 @@ -36,8 +37,9 @@ enum EmptyEvt { }; } // namespace BLECharacteristicEvt -class BLECharacteristic : public EventEmitter, uint16_t>, - public EventEmitter { +// Base class for BLE characteristics +// Specialized classes with EventEmitter support are generated per-UUID in the build process +class BLECharacteristic { public: BLECharacteristic(ESPBTUUID uuid, uint32_t properties); ~BLECharacteristic(); @@ -76,7 +78,19 @@ class BLECharacteristic : public EventEmitter, uint16_t)> &&listener) { + return INVALID_LISTENER_ID; + } + virtual EventEmitterListenerID on_read(std::function &&listener) { return INVALID_LISTENER_ID; } + virtual void off_write(EventEmitterListenerID id) {} + virtual void off_read(EventEmitterListenerID id) {} + protected: + // Virtual methods for emitting events - overridden by generated specialized classes + virtual void emit_on_write_(std::span value, uint16_t conn_id) {} + virtual void emit_on_read_(uint16_t conn_id) {} + bool write_event_{false}; BLEService *service_{}; ESPBTUUID uuid_; diff --git a/esphome/components/esp32_ble_server/ble_descriptor.cpp b/esphome/components/esp32_ble_server/ble_descriptor.cpp index afbe579513..dbfa8bc632 100644 --- a/esphome/components/esp32_ble_server/ble_descriptor.cpp +++ b/esphome/components/esp32_ble_server/ble_descriptor.cpp @@ -74,9 +74,7 @@ void BLEDescriptor::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_ break; this->value_.attr_len = param->write.len; memcpy(this->value_.attr_value, param->write.value, param->write.len); - this->emit_(BLEDescriptorEvt::VectorEvt::ON_WRITE, - std::vector(param->write.value, param->write.value + param->write.len), - param->write.conn_id); + this->emit_on_write_(std::span(param->write.value, param->write.len), param->write.conn_id); break; } default: diff --git a/esphome/components/esp32_ble_server/ble_descriptor.h b/esphome/components/esp32_ble_server/ble_descriptor.h index 8d3c22c5a1..1b787f7a35 100644 --- a/esphome/components/esp32_ble_server/ble_descriptor.h +++ b/esphome/components/esp32_ble_server/ble_descriptor.h @@ -8,6 +8,7 @@ #include #include +#include namespace esphome { namespace esp32_ble_server { @@ -24,7 +25,9 @@ enum VectorEvt { }; } // namespace BLEDescriptorEvt -class BLEDescriptor : public EventEmitter, uint16_t> { +// Base class for BLE descriptors +// Specialized classes with EventEmitter support are generated per-UUID in the build process +class BLEDescriptor { public: BLEDescriptor(ESPBTUUID uuid, uint16_t max_len = 100, bool read = true, bool write = true); virtual ~BLEDescriptor(); @@ -39,7 +42,16 @@ class BLEDescriptor : public EventEmitterstate_ == CREATED; } bool is_failed() { return this->state_ == FAILED; } + // Event listener registration - overridden by generated specialized classes if needed + virtual EventEmitterListenerID on_write(std::function, uint16_t)> &&listener) { + return INVALID_LISTENER_ID; + } + virtual void off_write(EventEmitterListenerID id) {} + protected: + // Virtual method for emitting events - overridden by generated specialized classes + virtual void emit_on_write_(std::span value, uint16_t conn_id) {} + BLECharacteristic *characteristic_{nullptr}; ESPBTUUID uuid_; uint16_t handle_{0xFFFF}; diff --git a/esphome/components/esp32_ble_server/ble_server.cpp b/esphome/components/esp32_ble_server/ble_server.cpp index 89299bb417..d73c343b4d 100644 --- a/esphome/components/esp32_ble_server/ble_server.cpp +++ b/esphome/components/esp32_ble_server/ble_server.cpp @@ -153,14 +153,14 @@ void BLEServer::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t ga case ESP_GATTS_CONNECT_EVT: { ESP_LOGD(TAG, "BLE Client connected"); this->add_client_(param->connect.conn_id); - this->emit_(BLEServerEvt::EmptyEvt::ON_CONNECT, param->connect.conn_id); + this->emit_on_connect_(param->connect.conn_id); break; } case ESP_GATTS_DISCONNECT_EVT: { ESP_LOGD(TAG, "BLE Client disconnected"); this->remove_client_(param->disconnect.conn_id); this->parent_->advertising_start(); - this->emit_(BLEServerEvt::EmptyEvt::ON_DISCONNECT, param->disconnect.conn_id); + this->emit_on_disconnect_(param->disconnect.conn_id); break; } case ESP_GATTS_REG_EVT: { diff --git a/esphome/components/esp32_ble_server/ble_server.h b/esphome/components/esp32_ble_server/ble_server.h index b5973ed099..e3181a945d 100644 --- a/esphome/components/esp32_ble_server/ble_server.h +++ b/esphome/components/esp32_ble_server/ble_server.h @@ -31,11 +31,9 @@ enum EmptyEvt { }; } // namespace BLEServerEvt -class BLEServer : public Component, - public GATTsEventHandler, - public BLEStatusEventHandler, - public Parented, - public EventEmitter { +// Base class for BLE server +// Note: Only one BLEServer instance exists per build, so we can use fixed defines +class BLEServer : public Component, public GATTsEventHandler, public BLEStatusEventHandler, public Parented { public: void setup() override; void loop() override; @@ -65,7 +63,16 @@ class BLEServer : public Component, void ble_before_disabled_event_handler() override; + // Event listener registration - overridden by generated specialized classes if needed + virtual EventEmitterListenerID on_connect(std::function &&listener) { return INVALID_LISTENER_ID; } + virtual EventEmitterListenerID on_disconnect(std::function &&listener) { return INVALID_LISTENER_ID; } + virtual void off_connect(EventEmitterListenerID id) {} + virtual void off_disconnect(EventEmitterListenerID id) {} + protected: + // Virtual methods for emitting events + virtual void emit_on_connect_(uint16_t conn_id) {} + virtual void emit_on_disconnect_(uint16_t conn_id) {} struct ServiceEntry { ESPBTUUID uuid; uint8_t inst_id; diff --git a/esphome/components/esp32_ble_server/ble_server_automations.cpp b/esphome/components/esp32_ble_server/ble_server_automations.cpp index 67e00a9bfe..afa958a4a0 100644 --- a/esphome/components/esp32_ble_server/ble_server_automations.cpp +++ b/esphome/components/esp32_ble_server/ble_server_automations.cpp @@ -14,9 +14,10 @@ Trigger, uint16_t> *BLETriggers::create_characteristic_on_w BLECharacteristic *characteristic) { Trigger, uint16_t> *on_write_trigger = // NOLINT(cppcoreguidelines-owning-memory) new Trigger, uint16_t>(); - characteristic->EventEmitter, uint16_t>::on( - BLECharacteristicEvt::VectorEvt::ON_WRITE, - [on_write_trigger](const std::vector &data, uint16_t id) { on_write_trigger->trigger(data, id); }); + characteristic->on_write([on_write_trigger](std::span data, uint16_t id) { + // Convert span to vector for trigger + on_write_trigger->trigger(std::vector(data.begin(), data.end()), id); + }); return on_write_trigger; } #endif @@ -25,9 +26,10 @@ Trigger, uint16_t> *BLETriggers::create_characteristic_on_w Trigger, uint16_t> *BLETriggers::create_descriptor_on_write_trigger(BLEDescriptor *descriptor) { Trigger, uint16_t> *on_write_trigger = // NOLINT(cppcoreguidelines-owning-memory) new Trigger, uint16_t>(); - descriptor->on( - BLEDescriptorEvt::VectorEvt::ON_WRITE, - [on_write_trigger](const std::vector &data, uint16_t id) { on_write_trigger->trigger(data, id); }); + descriptor->on_write([on_write_trigger](std::span data, uint16_t id) { + // Convert span to vector for trigger + on_write_trigger->trigger(std::vector(data.begin(), data.end()), id); + }); return on_write_trigger; } #endif @@ -35,8 +37,7 @@ Trigger, uint16_t> *BLETriggers::create_descriptor_on_write #ifdef USE_ESP32_BLE_SERVER_ON_CONNECT Trigger *BLETriggers::create_server_on_connect_trigger(BLEServer *server) { Trigger *on_connect_trigger = new Trigger(); // NOLINT(cppcoreguidelines-owning-memory) - server->on(BLEServerEvt::EmptyEvt::ON_CONNECT, - [on_connect_trigger](uint16_t conn_id) { on_connect_trigger->trigger(conn_id); }); + server->on_connect([on_connect_trigger](uint16_t conn_id) { on_connect_trigger->trigger(conn_id); }); return on_connect_trigger; } #endif @@ -44,8 +45,7 @@ Trigger *BLETriggers::create_server_on_connect_trigger(BLEServer *serv #ifdef USE_ESP32_BLE_SERVER_ON_DISCONNECT Trigger *BLETriggers::create_server_on_disconnect_trigger(BLEServer *server) { Trigger *on_disconnect_trigger = new Trigger(); // NOLINT(cppcoreguidelines-owning-memory) - server->on(BLEServerEvt::EmptyEvt::ON_DISCONNECT, - [on_disconnect_trigger](uint16_t conn_id) { on_disconnect_trigger->trigger(conn_id); }); + server->on_disconnect([on_disconnect_trigger](uint16_t conn_id) { on_disconnect_trigger->trigger(conn_id); }); return on_disconnect_trigger; } #endif @@ -58,8 +58,7 @@ void BLECharacteristicSetValueActionManager::set_listener(BLECharacteristic *cha auto *existing = this->find_listener_(characteristic); if (existing != nullptr) { // Remove the previous listener - characteristic->EventEmitter::off(BLECharacteristicEvt::EmptyEvt::ON_READ, - existing->listener_id); + characteristic->off_read(existing->listener_id); // Remove the pre-notify listener this->off(BLECharacteristicSetValueActionEvt::PRE_NOTIFY, existing->pre_notify_listener_id); // Remove from vector diff --git a/esphome/components/esp32_ble_server/ble_server_automations.h b/esphome/components/esp32_ble_server/ble_server_automations.h index 8fcb5842c3..08a3322367 100644 --- a/esphome/components/esp32_ble_server/ble_server_automations.h +++ b/esphome/components/esp32_ble_server/ble_server_automations.h @@ -46,8 +46,12 @@ enum BLECharacteristicSetValueActionEvt { }; // Class to make sure only one BLECharacteristicSetValueAction is active at a time for each characteristic +#ifndef BLE_SET_VALUE_ACTION_MAX_LISTENERS +#define BLE_SET_VALUE_ACTION_MAX_LISTENERS 1 +#endif + class BLECharacteristicSetValueActionManager - : public EventEmitter { + : public EventEmitter { public: // Singleton pattern static BLECharacteristicSetValueActionManager *get_instance() { @@ -92,11 +96,10 @@ template class BLECharacteristicSetValueAction : public Actionparent_->set_value(this->buffer_.value(x...)); // Set the listener for read events - this->listener_id_ = this->parent_->EventEmitter::on( - BLECharacteristicEvt::EmptyEvt::ON_READ, [this, x...](uint16_t id) { - // Set the value of the characteristic every time it is read - this->parent_->set_value(this->buffer_.value(x...)); - }); + this->listener_id_ = this->parent_->on_read([this, x...](uint16_t id) { + // Set the value of the characteristic every time it is read + this->parent_->set_value(this->buffer_.value(x...)); + }); // Set the listener in the global manager so only one BLECharacteristicSetValueAction is set for each characteristic BLECharacteristicSetValueActionManager::get_instance()->set_listener( this->parent_, this->listener_id_, [this, x...]() { this->parent_->set_value(this->buffer_.value(x...)); }); diff --git a/esphome/components/esp32_ble_server/ble_service.cpp b/esphome/components/esp32_ble_server/ble_service.cpp index 96fedf2346..3c5b40ba9e 100644 --- a/esphome/components/esp32_ble_server/ble_service.cpp +++ b/esphome/components/esp32_ble_server/ble_service.cpp @@ -41,6 +41,10 @@ BLECharacteristic *BLEService::create_characteristic(ESPBTUUID uuid, esp_gatt_ch return characteristic; } +void BLEService::add_characteristic(BLECharacteristic *characteristic) { + this->characteristics_.push_back(characteristic); +} + void BLEService::do_create(BLEServer *server) { this->server_ = server; diff --git a/esphome/components/esp32_ble_server/ble_service.h b/esphome/components/esp32_ble_server/ble_service.h index dcfad5f501..311c937c28 100644 --- a/esphome/components/esp32_ble_server/ble_service.h +++ b/esphome/components/esp32_ble_server/ble_service.h @@ -31,6 +31,9 @@ class BLEService { BLECharacteristic *create_characteristic(uint16_t uuid, esp_gatt_char_prop_t properties); BLECharacteristic *create_characteristic(ESPBTUUID uuid, esp_gatt_char_prop_t properties); + // Add pre-constructed characteristic (used by generated code) + void add_characteristic(BLECharacteristic *characteristic); + ESPBTUUID get_uuid() { return this->uuid_; } uint8_t get_inst_id() { return this->inst_id_; } BLECharacteristic *get_last_created_characteristic() { return this->last_created_characteristic_; } diff --git a/esphome/components/esp32_improv/esp32_improv_component.cpp b/esphome/components/esp32_improv/esp32_improv_component.cpp index ca08ff0cca..f773083890 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.cpp +++ b/esphome/components/esp32_improv/esp32_improv_component.cpp @@ -38,8 +38,7 @@ void ESP32ImprovComponent::setup() { }); } #endif - global_ble_server->on(BLEServerEvt::EmptyEvt::ON_DISCONNECT, - [this](uint16_t conn_id) { this->set_error_(improv::ERROR_NONE); }); + global_ble_server->on_disconnect([this](uint16_t conn_id) { this->set_error_(improv::ERROR_NONE); }); // Start with loop disabled - will be enabled by start() when needed this->disable_loop(); @@ -57,12 +56,11 @@ void ESP32ImprovComponent::setup_characteristics() { this->error_->add_descriptor(error_descriptor); this->rpc_ = this->service_->create_characteristic(improv::RPC_COMMAND_UUID, BLECharacteristic::PROPERTY_WRITE); - this->rpc_->EventEmitter, uint16_t>::on( - BLECharacteristicEvt::VectorEvt::ON_WRITE, [this](const std::vector &data, uint16_t id) { - if (!data.empty()) { - this->incoming_data_.insert(this->incoming_data_.end(), data.begin(), data.end()); - } - }); + this->rpc_->on_write([this](std::span data, uint16_t id) { + if (!data.empty()) { + this->incoming_data_.insert(this->incoming_data_.end(), data.begin(), data.end()); + } + }); BLEDescriptor *rpc_descriptor = new BLE2902(); this->rpc_->add_descriptor(rpc_descriptor); diff --git a/esphome/components/event_emitter/event_emitter.h b/esphome/components/event_emitter/event_emitter.h index 74afde03c0..e44cac7cc7 100644 --- a/esphome/components/event_emitter/event_emitter.h +++ b/esphome/components/event_emitter/event_emitter.h @@ -1,5 +1,5 @@ #pragma once -#include +#include #include #include @@ -13,34 +13,31 @@ static constexpr EventEmitterListenerID INVALID_LISTENER_ID = 0; // EventEmitter class that can emit events with a specific name (it is highly recommended to use an enum class for this) // and a list of arguments. Supports multiple listeners for each event. -template class EventEmitter { +// MaxListeners is the compile-time maximum number of listeners per event type. +template class EventEmitter { public: EventEmitterListenerID on(EvtType event, std::function listener) { - EventEmitterListenerID listener_id = this->get_next_id_(); - - // Find or create event entry - EventEntry *entry = this->find_or_create_event_(event); - entry->listeners.push_back({listener_id, listener}); - - return listener_id; + // Find a free slot in the listeners array + for (auto &entry : this->listeners_) { + if (entry.id == INVALID_LISTENER_ID) { + // Found empty slot + EventEmitterListenerID listener_id = this->get_next_id_(); + entry.id = listener_id; + entry.event = event; + entry.callback = std::move(listener); + return listener_id; + } + } + // No free slots - array is full + return INVALID_LISTENER_ID; } void off(EvtType event, EventEmitterListenerID id) { - EventEntry *entry = this->find_event_(event); - if (entry == nullptr) - return; - - // Remove listener with given id - for (auto it = entry->listeners.begin(); it != entry->listeners.end(); ++it) { - if (it->id == id) { - // Swap with last and pop for efficient removal - *it = entry->listeners.back(); - entry->listeners.pop_back(); - - // Remove event entry if no more listeners - if (entry->listeners.empty()) { - this->remove_event_(event); - } + // Find and remove listener with given id + for (auto &entry : this->listeners_) { + if (entry.id == id && entry.event == event) { + entry.id = INVALID_LISTENER_ID; + entry.callback = nullptr; return; } } @@ -48,25 +45,19 @@ template class EventEmitter { protected: void emit_(EvtType event, Args... args) { - EventEntry *entry = this->find_event_(event); - if (entry == nullptr) - return; - // Call all listeners for this event - for (const auto &listener : entry->listeners) { - listener.callback(args...); + for (const auto &entry : this->listeners_) { + if (entry.id != INVALID_LISTENER_ID && entry.event == event) { + entry.callback(args...); + } } } private: - struct Listener { - EventEmitterListenerID id; - std::function callback; - }; - - struct EventEntry { + struct ListenerEntry { EvtType event; - std::vector listeners; + EventEmitterListenerID id{INVALID_LISTENER_ID}; + std::function callback{nullptr}; }; EventEmitterListenerID get_next_id_() { @@ -79,38 +70,8 @@ template class EventEmitter { return this->current_id_; } - EventEntry *find_event_(EvtType event) { - for (auto &entry : this->events_) { - if (entry.event == event) { - return &entry; - } - } - return nullptr; - } - - EventEntry *find_or_create_event_(EvtType event) { - EventEntry *entry = this->find_event_(event); - if (entry != nullptr) - return entry; - - // Create new event entry - this->events_.push_back({event, {}}); - return &this->events_.back(); - } - - void remove_event_(EvtType event) { - for (auto it = this->events_.begin(); it != this->events_.end(); ++it) { - if (it->event == event) { - // Swap with last and pop - *it = this->events_.back(); - this->events_.pop_back(); - return; - } - } - } - - std::vector events_; - EventEmitterListenerID current_id_ = 0; + std::array listeners_{}; + EventEmitterListenerID current_id_{0}; }; } // namespace event_emitter