Let the fit helper pick the store from both flash facts, guard the crash handler words, add tests

This commit is contained in:
J. Nick Koston
2026-10-13 14:07:54 -10:00
parent eb7a741c24
commit 44e962ab96
3 changed files with 47 additions and 9 deletions
+12 -7
View File
@@ -37,15 +37,20 @@ def preference_words(value_bytes: int) -> int:
return (value_bytes + 3) // 4 + 1
def preference_slots(value_bytes: int, restore_from_flash: bool) -> int:
"""How many values of this size fit, given where restore_from_flash sends them."""
def preference_slots(value_bytes: int, in_flash: bool, restore_from_flash: bool) -> int:
"""How many values of this size fit where make_preference() puts them."""
words = preference_words(value_bytes)
regions = (
(PREFERENCES_FLASH_WORDS,)
if restore_from_flash
else (PREFERENCES_RTC_NORMAL_WORDS, PREFERENCES_RTC_EBOOT_WORDS)
if in_flash or restore_from_flash:
store = (
PREFERENCES_FLASH_WORDS
if restore_from_flash
else PREFERENCES_FLASH_WORDS_WITHOUT_RESTORE
)
return store // words
return sum(
region // words
for region in (PREFERENCES_RTC_NORMAL_WORDS, PREFERENCES_RTC_EBOOT_WORDS)
)
return sum(region // words for region in regions)
# esp8266 namespace is already defined by arduino, manually prefix esphome
+4 -2
View File
@@ -27,7 +27,9 @@ static constexpr uint32_t ESP_RTC_USER_MEM_SIZE_BYTES = ESP_RTC_USER_MEM_SIZE_WO
static constexpr uint32_t RTC_EBOOT_REGION_WORDS = ESP8266_PREFERENCES_RTC_EBOOT_WORDS;
static constexpr uint32_t RTC_NORMAL_REGION_WORDS = ESP8266_PREFERENCES_RTC_NORMAL_WORDS;
static constexpr uint32_t PREF_TOTAL_WORDS = RTC_EBOOT_REGION_WORDS + RTC_NORMAL_REGION_WORDS; // 110
static_assert(PREF_TOTAL_WORDS <= ESP_RTC_USER_MEM_SIZE_WORDS, "preference regions overflow RTC memory");
static constexpr uint32_t RTC_CRASH_HANDLER_WORDS = 18; // Words 110-127, see crash_handler.cpp
static_assert(PREF_TOTAL_WORDS + RTC_CRASH_HANDLER_WORDS <= ESP_RTC_USER_MEM_SIZE_WORDS,
"preference regions overlap the crash handler's RTC words");
// Maximum preference size in words (limited by uint8_t length_words field)
static constexpr uint32_t MAX_PREFERENCE_WORDS = 255;
@@ -54,7 +56,7 @@ static inline bool esp_rtc_user_mem_write(uint32_t index, uint32_t value) {
if (index >= ESP_RTC_USER_MEM_SIZE_WORDS) {
return false;
}
if (index < 32 && s_prevent_write) {
if (index < RTC_EBOOT_REGION_WORDS && s_prevent_write) {
return false;
}
@@ -0,0 +1,31 @@
"""The config-time fit rule must match make_preference() in preferences.cpp."""
import pytest
from esphome.components.esp8266.const import preference_slots, preference_words
@pytest.mark.parametrize(
("value_bytes", "words"), [(0, 1), (1, 2), (4, 2), (5, 3), (94, 25)]
)
def test_preference_words_rounds_up_and_adds_the_checksum(
value_bytes: int, words: int
) -> None:
assert preference_words(value_bytes) == words
def test_rtc_values_cannot_straddle_the_regions() -> None:
# 25 words each: 3 fit in the 78-word region and 1 in the 32-word one
assert preference_slots(94, in_flash=False, restore_from_flash=False) == 4
# 32 words each: 2 + 1, where 110 // 32 would also say 3
assert preference_slots(124, in_flash=False, restore_from_flash=False) == 3
# 39 words each: 2 + 0, where 110 // 39 would say 2 as well but the eboot region is skipped
assert preference_slots(152, in_flash=False, restore_from_flash=False) == 2
# Larger than the biggest region
assert preference_slots(400, in_flash=False, restore_from_flash=False) == 0
def test_flash_store_size_follows_restore_from_flash() -> None:
assert preference_slots(94, in_flash=True, restore_from_flash=True) == 5
assert preference_slots(94, in_flash=False, restore_from_flash=True) == 5
assert preference_slots(94, in_flash=True, restore_from_flash=False) == 2