[core] Add RAMAllocator::make_unique for objects whose allocation may fail (#19245)

This commit is contained in:
J. Nick Koston
2026-09-14 13:12:25 +12:00
committed by Jesse Hills
parent 1a555d5848
commit cd5d4ff254
2 changed files with 92 additions and 0 deletions
+52
View File
@@ -367,4 +367,56 @@ TEST(FixedVectorTryInit, ReportsExhaustionAndStaysEmpty) {
EXPECT_EQ(v.size(), 1u);
}
// --- RAMAllocator::make_unique() ---
namespace {
struct Probe {
static inline int live = 0;
int a;
int b;
Probe(int a, int b) : a(a), b(b) { live++; }
~Probe() { live--; }
};
} // namespace
static_assert(sizeof(RAMUniquePtr<Probe>) == sizeof(Probe *), "the deleter must not add storage");
TEST(RAMAllocatorMakeUnique, ForwardsArgsAndDestroysOnce) {
auto p = RAMAllocator<Probe>().make_unique(3, 4);
ASSERT_NE(p, nullptr);
EXPECT_EQ(p->a, 3);
EXPECT_EQ(p->b, 4);
EXPECT_EQ(Probe::live, 1);
p.reset();
EXPECT_EQ(Probe::live, 0);
}
TEST(RAMAllocatorMakeUnique, ValueInitializesLikeMakeUnique) {
struct Plain {
uint32_t words[8];
};
// Dirty a block of the same size first so a recycled allocation is not zero by chance
auto dirty = RAMAllocator<uint8_t>().make_unique_array_for_overwrite(sizeof(Plain));
std::memset(dirty.get(), 0xFF, sizeof(Plain));
dirty.reset();
auto p = RAMAllocator<Plain>().make_unique();
ASSERT_NE(p, nullptr);
// Under ASan fresh blocks are filled with 0xbe, so this holds even when the dirtied block is not reused
EXPECT_TRUE(std::all_of(std::begin(p->words), std::end(p->words), [](uint32_t w) { return w == 0; }));
}
TEST(RAMAllocatorMakeUnique, ArrayFormRejectsOverflowAndZero) {
EXPECT_EQ(RAMAllocator<uint32_t>().make_unique_array_for_overwrite(SIZE_MAX / sizeof(uint32_t) + 1), nullptr);
EXPECT_EQ(RAMAllocator<uint32_t>().make_unique_array_for_overwrite(0), nullptr);
EXPECT_NE(RAMAllocator<uint32_t>().make_unique_array_for_overwrite(1), nullptr);
}
TEST(RAMAllocatorMakeUnique, ArrayFormAllocatesElements) {
RAMUniquePtr<uint8_t[]> buf = RAMAllocator<uint8_t>().make_unique_array_for_overwrite(256);
ASSERT_NE(buf, nullptr);
std::memset(buf.get(), 0xA5, 256);
EXPECT_EQ(buf[0], 0xA5);
EXPECT_EQ(buf[255], 0xA5);
}
} // namespace esphome::core::testing