-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Handle allocator propagation in basic_memory_buffer::move, Fix #4487 #4490
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ce2f6b8
49f8b4c
7e8a9b9
fe8d8b6
55e1358
4e219ee
5cd0e40
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -307,7 +307,7 @@ TEST(memory_buffer_test, ctor) { | |
| EXPECT_EQ(123u, buffer.capacity()); | ||
| } | ||
|
|
||
| using std_allocator = allocator_ref<std::allocator<char>>; | ||
| using std_allocator = allocator_ref<std::allocator<char>, true>; | ||
|
|
||
| TEST(memory_buffer_test, move_ctor_inline_buffer) { | ||
| auto check_move_buffer = | ||
|
|
@@ -351,6 +351,56 @@ TEST(memory_buffer_test, move_ctor_dynamic_buffer) { | |
| EXPECT_GT(buffer2.capacity(), 4u); | ||
| } | ||
|
|
||
| using std_allocator_n = allocator_ref<std::allocator<char>, false>; | ||
|
|
||
| TEST(memory_buffer_test, move_ctor_inline_buffer_non_propagating) { | ||
| auto check_move_buffer = | ||
| [](const char* str, | ||
| basic_memory_buffer<char, 5, std_allocator_n>& buffer) { | ||
| std::allocator<char>* original_alloc_ptr = buffer.get_allocator().get(); | ||
| const char* original_data_ptr = &buffer[0]; | ||
| basic_memory_buffer<char, 5, std_allocator_n> buffer2( | ||
| std::move(buffer)); | ||
| const char* new_data_ptr = &buffer2[0]; | ||
| EXPECT_NE(original_data_ptr, new_data_ptr); | ||
| EXPECT_EQ(str, std::string(&buffer[0], buffer.size())); | ||
| EXPECT_EQ(str, std::string(&buffer2[0], buffer2.size())); | ||
| EXPECT_EQ(5u, buffer2.capacity()); | ||
| // Allocators should NOT be transferred; they remain distinct instances. | ||
| // The original buffer's allocator pointer should still be valid (not | ||
| // nullptr). | ||
| EXPECT_EQ(original_alloc_ptr, buffer.get_allocator().get()); | ||
| EXPECT_NE(original_alloc_ptr, buffer2.get_allocator().get()); | ||
| }; | ||
| auto alloc = std::allocator<char>(); | ||
| basic_memory_buffer<char, 5, std_allocator_n> buffer( | ||
| (std_allocator_n(&alloc))); | ||
| const char test[] = "test"; | ||
| buffer.append(string_view(test, 4)); | ||
| check_move_buffer("test", buffer); | ||
| buffer.push_back('a'); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The test you're referring to specifically targets the inline buffer, and for that reason, the buffer should remain unchanged. Its purpose is to verify that the content stays the same and that no heap allocation occurs as long as it's within the capacity limit. If you take a look at the move implementation in format.h, you'll see that when an inline buffer is used, the content is simply copied. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes I understand that buffer sized under small buffer optimizaion threshold is simply copied, but that's not the question. The question is the same as in the line 366 comment thread above. Which essentially is: is the fact that If it's a part of interface, both these checks are correct and even required. If it's not, we are testing implementation detail assumptions here which don't have to hold, even though the checks are momentarily correct. There is no doc about whether it's an interface guarantee, or a comment in the code, that's why I assume the latter. @vitaut has the ultimate say here however.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @dlex made a good point.
It is definitely an implementation detail and we shouldn't rely on it here.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But this can be done as a follow-up. |
||
| check_move_buffer("testa", buffer); | ||
vitaut marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| TEST(memory_buffer_test, move_ctor_dynamic_buffer_non_propagating) { | ||
| auto alloc = std::allocator<char>(); | ||
| basic_memory_buffer<char, 4, std_allocator_n> buffer( | ||
| (std_allocator_n(&alloc))); | ||
| const char test[] = "test"; | ||
| buffer.append(test, test + 4); | ||
| const char* inline_buffer_ptr = &buffer[0]; | ||
| buffer.push_back('a'); | ||
| EXPECT_NE(&buffer[0], inline_buffer_ptr); | ||
| std::allocator<char>* original_alloc_ptr = buffer.get_allocator().get(); | ||
| basic_memory_buffer<char, 4, std_allocator_n> buffer2; | ||
| buffer2 = std::move(buffer); | ||
| EXPECT_EQ(std::string(&buffer2[0], buffer2.size()), "testa"); | ||
| EXPECT_GT(buffer2.capacity(), 4u); | ||
| EXPECT_NE(&buffer2[0], inline_buffer_ptr); | ||
| EXPECT_EQ(original_alloc_ptr, buffer.get_allocator().get()); | ||
| EXPECT_NE(original_alloc_ptr, buffer2.get_allocator().get()); | ||
| } | ||
|
|
||
| void check_move_assign_buffer(const char* str, | ||
| basic_memory_buffer<char, 5>& buffer) { | ||
| basic_memory_buffer<char, 5> buffer2; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AFAIU, this check is here to verify that the underlying memory buffer have not been moved from
buffertobuffer2, and it's used to verify that memory copying has happened instead. You may want to consider a more black box way to test that: compare a copy of&buffer[0]before the move, and&buffer2[0]after the move, they should be different. And vice versa: where we should expect the underlying buffer has been moved, they should be the same.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What you suggest is a great idea, but I also believe it's essential to verify the contents as well. Since we're copying, it's crucial to ensure the contents remain the same. That said, I will apply your recommendation by adding:
EXPECT_NE(original_data_ptr, new_data_ptr);