From bccaee8d7992309c94123fc0679e33c11e6e1d3a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Mar 2026 13:38:25 -1000 Subject: [PATCH 1/3] fix test --- tests/dummy_main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/dummy_main.cpp b/tests/dummy_main.cpp index 6fa0c08aa3..329286e2fa 100644 --- a/tests/dummy_main.cpp +++ b/tests/dummy_main.cpp @@ -15,7 +15,7 @@ void setup() { static char name[] = "livingroom"; static char friendly_name[] = "LivingRoom"; App.pre_setup(name, sizeof(name) - 1, friendly_name, sizeof(friendly_name) - 1); - auto *log = new logger::Logger(115200); // NOLINT + auto *log = new logger::Logger(115200, 512); // NOLINT log->pre_setup(); log->set_uart_selection(logger::UART_SELECTION_UART0); App.register_component_(log); From 8b89f5e036139a8c953ef515119d8ab8ede0b87c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Mar 2026 13:54:00 -1000 Subject: [PATCH 2/3] avoid template overhead --- esphome/core/helpers.h | 21 ------------------- esphome/cpp_generator.py | 10 +++++---- .../deep_sleep/test_deep_sleep.py | 2 +- tests/component_tests/globals/test_globals.py | 6 +++--- tests/component_tests/image/test_init.py | 8 +++++-- .../mipi_dsi/test_mipi_dsi_config.py | 5 +++-- .../status_led/test_status_led.py | 4 ++-- 7 files changed, 21 insertions(+), 35 deletions(-) diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 6d6a31749d..e0d6fe56b1 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -2222,27 +2222,6 @@ template U> T clamp_at_most(T value, return value; } -/** - * @brief Provides properly aligned, uninitialized static storage for a given type T. - * - * This struct is designed to replace dynamic heap allocations (`new T(...)`) for - * global or static singletons within ESPHome, preventing memory fragmentation. - * The underlying object must be explicitly constructed using placement new - * before access. No destructor is called — this is intentional since ESPHome - * singletons live for the entire device lifetime. - */ -template struct PlacementStorage { - /// @brief Raw byte storage, strictly aligned for type T. - alignas(T) unsigned char data[sizeof(T)]; - - /// @brief Retrieves a pointer to the storage as the target type. - /// The caller must ensure the object has been constructed via placement new before dereferencing. - T *get() { return reinterpret_cast(data); } - - /// @brief Retrieves a const pointer to the storage as the target type. - const T *get() const { return reinterpret_cast(data); } -}; - /// @name Internal functions ///@{ diff --git a/esphome/cpp_generator.py b/esphome/cpp_generator.py index 5c7da6e1c1..530bdcc05c 100644 --- a/esphome/cpp_generator.py +++ b/esphome/cpp_generator.py @@ -584,18 +584,20 @@ def Pvariable(id_: ID, rhs: SafeExpType, type_: "MockObj" = None) -> "MockObj": # For 'new' allocations, use placement new into static storage # to avoid heap fragmentation on embedded devices. the_type = id_.type - storage_type = MockObj(f"esphome::PlacementStorage<{the_type}>") - storage_id = ID(f"{id_.id}_storage_", type=storage_type) + storage_name = f"{id_.id}_storage_" + # Declare aligned byte array for the object storage CORE.add_global( - VariableDeclarationExpression(storage_type, "", storage_id, static=True) + RawStatement( + f"alignas({the_type}) static unsigned char {storage_name}[sizeof({the_type})];" + ) ) CORE.add_global( AssignmentExpression( f"static {the_type}", "*const ", id_, - MockObj(f"{storage_id.id}.get()"), + MockObj(f"reinterpret_cast<{the_type} *>({storage_name})"), ) ) # Extract args from the CallExpression and rebuild as placement new. diff --git a/tests/component_tests/deep_sleep/test_deep_sleep.py b/tests/component_tests/deep_sleep/test_deep_sleep.py index d78848872b..2ebabd4bbd 100644 --- a/tests/component_tests/deep_sleep/test_deep_sleep.py +++ b/tests/component_tests/deep_sleep/test_deep_sleep.py @@ -8,7 +8,7 @@ def test_deep_sleep_setup(generate_main): main_cpp = generate_main("tests/component_tests/deep_sleep/test_deep_sleep1.yaml") assert ( - "static deep_sleep::DeepSleepComponent *const deepsleep = deepsleep_storage_.get();" + "static deep_sleep::DeepSleepComponent *const deepsleep = reinterpret_cast(deepsleep_storage_);" in main_cpp ) assert "new(deepsleep) deep_sleep::DeepSleepComponent();" in main_cpp diff --git a/tests/component_tests/globals/test_globals.py b/tests/component_tests/globals/test_globals.py index 3dec3b1615..04fd6d5f7d 100644 --- a/tests/component_tests/globals/test_globals.py +++ b/tests/component_tests/globals/test_globals.py @@ -16,12 +16,12 @@ def test_globals_placement_new_with_template_args( # Globals uses Pvariable with Type.new(template_args, initial_value) # which exercises the template_args preservation in placement new. assert "static globals::GlobalsComponent *const my_global_int" in main_cpp - assert "PlacementStorage>" in main_cpp + assert "sizeof(globals::GlobalsComponent)" in main_cpp assert "new(my_global_int) globals::GlobalsComponent" in main_cpp # Verify initial value is passed as constructor arg assert "42" in main_cpp # Check other globals are also generated - assert "PlacementStorage>" in main_cpp - assert "PlacementStorage>" in main_cpp + assert "sizeof(globals::GlobalsComponent)" in main_cpp + assert "sizeof(globals::GlobalsComponent)" in main_cpp diff --git a/tests/component_tests/image/test_init.py b/tests/component_tests/image/test_init.py index 3e554cff73..ea974ff892 100644 --- a/tests/component_tests/image/test_init.py +++ b/tests/component_tests/image/test_init.py @@ -242,9 +242,13 @@ def test_image_generation( main_cpp = generate_main(component_config_path("image_test.yaml")) assert "uint8_t_id[] PROGMEM = {0x24, 0x21, 0x24, 0x21" in main_cpp assert ( - "static esphome::PlacementStorage cat_img_storage_;" in main_cpp + "alignas(image::Image) static unsigned char cat_img_storage_[sizeof(image::Image)];" + in main_cpp + ) + assert ( + "static image::Image *const cat_img = reinterpret_cast(cat_img_storage_);" + in main_cpp ) - assert "static image::Image *const cat_img = cat_img_storage_.get();" in main_cpp assert ( "new(cat_img) image::Image(uint8_t_id, 32, 24, image::IMAGE_TYPE_RGB565, image::TRANSPARENCY_OPAQUE);" in main_cpp diff --git a/tests/component_tests/mipi_dsi/test_mipi_dsi_config.py b/tests/component_tests/mipi_dsi/test_mipi_dsi_config.py index eba6305a48..aaf5ff4156 100644 --- a/tests/component_tests/mipi_dsi/test_mipi_dsi_config.py +++ b/tests/component_tests/mipi_dsi/test_mipi_dsi_config.py @@ -119,11 +119,12 @@ def test_code_generation( main_cpp = generate_main(component_fixture_path("mipi_dsi.yaml")) assert ( - "static esphome::PlacementStorage p4_nano_storage_;" + "alignas(mipi_dsi::MIPI_DSI) static unsigned char p4_nano_storage_[sizeof(mipi_dsi::MIPI_DSI)];" in main_cpp ) assert ( - "static mipi_dsi::MIPI_DSI *const p4_nano = p4_nano_storage_.get();" in main_cpp + "static mipi_dsi::MIPI_DSI *const p4_nano = reinterpret_cast(p4_nano_storage_);" + in main_cpp ) assert ( "new(p4_nano) mipi_dsi::MIPI_DSI(800, 1280, display::COLOR_BITNESS_565, 16);" diff --git a/tests/component_tests/status_led/test_status_led.py b/tests/component_tests/status_led/test_status_led.py index b40f9b8bcc..e7cdd6f29a 100644 --- a/tests/component_tests/status_led/test_status_led.py +++ b/tests/component_tests/status_led/test_status_led.py @@ -13,11 +13,11 @@ def test_status_led_generation( """Test status_led generation.""" main_cpp = generate_main(component_config_path("status_led_test.yaml")) assert ( - "static esphome::PlacementStorage status_led_statusled_id_storage_;" + "alignas(status_led::StatusLED) static unsigned char status_led_statusled_id_storage_[sizeof(status_led::StatusLED)];" in main_cpp ) assert ( - "static status_led::StatusLED *const status_led_statusled_id = status_led_statusled_id_storage_.get();" + "static status_led::StatusLED *const status_led_statusled_id = reinterpret_cast(status_led_statusled_id_storage_);" in main_cpp ) assert "new(status_led_statusled_id) status_led::StatusLED(" in main_cpp From 42e1d933989cad1299eb5095e9614bdcd41d8a76 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Mar 2026 13:57:08 -1000 Subject: [PATCH 3/3] cleanup --- esphome/core/helpers.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index e3fc6092c3..82c6b3833c 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -11,11 +11,9 @@ #include #include #include -#include #include #include #include -#include #include #include #include