refactor(buffer): rename 'additional' parameter to 'mem_to_free' for clarity

Header changes (include/libipc/buffer.h):
- Rename: additional → mem_to_free (better semantic name)
- Add documentation comments explaining the parameter's purpose
- Clarifies that mem_to_free is passed to destructor instead of p
- Use case: when data pointer is offset into a larger allocation

Implementation changes (src/libipc/buffer.cpp):
- Update parameter name in constructor implementation
- No logic changes, just naming improvement

Test changes (test/test_buffer.cpp):
- Fix TEST_F(BufferTest, ConstructorWithMemToFree)
- Previous test caused crash: passed stack variable address to destructor
- New test correctly demonstrates the parameter's purpose:
  * Allocate 100-byte block
  * Use offset portion (bytes 25-75) as data
  * Destructor receives original block pointer for proper cleanup
- Prevents double-free and invalid free errors

Semantic explanation:
  buffer(data_ptr, size, destructor, mem_to_free)

  On destruction:
    destructor(mem_to_free ? mem_to_free : data_ptr, size)

  This allows:
    char* block = new char[100];
    char* data = block + 25;
    buffer buf(data, 50, my_free, block);  // Frees 'block', not 'data'
This commit is contained in:
木头云 2025-11-30 05:09:56 +00:00
parent de76cf80d5
commit 91e4489a55
3 changed files with 21 additions and 9 deletions

View File

@ -17,7 +17,9 @@ public:
buffer(); buffer();
buffer(void* p, std::size_t s, destructor_t d); buffer(void* p, std::size_t s, destructor_t d);
buffer(void* p, std::size_t s, destructor_t d, void* additional); // mem_to_free: pointer to be passed to destructor (if different from p)
// Use case: when p points into a larger allocated block that needs to be freed
buffer(void* p, std::size_t s, destructor_t d, void* mem_to_free);
buffer(void* p, std::size_t s); buffer(void* p, std::size_t s);
template <std::size_t N> template <std::size_t N>

View File

@ -38,8 +38,8 @@ buffer::buffer(void* p, std::size_t s, destructor_t d)
: p_(p_->make(p, s, d, nullptr)) { : p_(p_->make(p, s, d, nullptr)) {
} }
buffer::buffer(void* p, std::size_t s, destructor_t d, void* additional) buffer::buffer(void* p, std::size_t s, destructor_t d, void* mem_to_free)
: p_(p_->make(p, s, d, additional)) { : p_(p_->make(p, s, d, mem_to_free)) {
} }
buffer::buffer(void* p, std::size_t s) buffer::buffer(void* p, std::size_t s)

View File

@ -73,16 +73,26 @@ TEST_F(BufferTest, DestructorCalled) {
EXPECT_EQ(DestructorTracker::count, 1); EXPECT_EQ(DestructorTracker::count, 1);
} }
// Test constructor with additional parameter // Test constructor with mem_to_free parameter
TEST_F(BufferTest, ConstructorWithAdditional) { // Scenario: allocate a large block, but only use a portion as data
char* data = new char[50]; TEST_F(BufferTest, ConstructorWithMemToFree) {
int additional_value = 42; // Allocate a block of 100 bytes
char* allocated_block = new char[100];
buffer buf(data, 50, DestructorTracker::destructor, &additional_value); // But only use the middle 50 bytes as data (offset 25)
char* data_start = allocated_block + 25;
std::strcpy(data_start, "Offset data");
// When destroyed, should free the entire allocated_block, not just data_start
buffer buf(data_start, 50, DestructorTracker::destructor, allocated_block);
EXPECT_FALSE(buf.empty()); EXPECT_FALSE(buf.empty());
EXPECT_EQ(buf.size(), 50u); EXPECT_EQ(buf.size(), 50u);
EXPECT_NE(buf.data(), nullptr); EXPECT_EQ(buf.data(), data_start);
EXPECT_STREQ(static_cast<const char*>(buf.data()), "Offset data");
// Destructor will be called with allocated_block (not data_start)
// This correctly frees the entire allocation
} }
// Test constructor without destructor // Test constructor without destructor