-
Notifications
You must be signed in to change notification settings - Fork 41
patina_internal_collections: Fix bounds checking in Storage::resize() #1247
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
base: main
Are you sure you want to change the base?
patina_internal_collections: Fix bounds checking in Storage::resize() #1247
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
df4da14 to
3d33aab
Compare
|
@garybeihl, for future reference to this PR. This is adding additional cases for the change previously made in a single location in #1243, right? |
Nvm, I see your comment in the previous PR now, that is the case. Thanks for updating these too. |
|
Resize requires that the new buffer is always equal to or larger than the old buffer. I think the bug is that you are even able to resize to a lower capacity. We have an assert in the resize code that should be preventing resizing to a lower capacity |
|
We cannot inherently resize to a lower capacity because there is no guarantee of ordering of nodes. Loosing any active node due to a resize to a lower capacity will actively destroy the tree. I guess that is, unless we resize capacity to a lower capacity that is still greater than the current size. But I thought my currently implementation just directly moves everything to the new buffer as is. We don't actually build a brand new tree. |
Shouldn't that assert be |
Yes, I agree that we should change it to that. I think that is the true bug. The buffer that makes up all of the nodes does not guarantee having all of the valid nodes at the start of the buffer. Looking at the
That means the buffer looks something like this:
So on top of us needing to change the assert above, as you suggested, we also need to loop through all nodes ( |
731575b to
64bdafb
Compare
|
Sounds good. I've made those changes and re-worded the PR title to more accurately reflect the changes made. Thanks for your guidance on this. |
Javagedes
left a comment
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.
Thank you for fixing all of my mistakes. This is great work :)
|
@garybeihl can you rename |
fb0637a to
ad17b4a
Compare
Done - please have a look - thanks! |
@garybeihl Looks good to me. There are a few documentation examples that look like they need updated. But once that is done I will merge :) |
## Description Changed resize() to properly enforce capacity constraints and copy all nodes including gaps: 1. Changed assert from self.len() to self.capacity() to prevent resizing to a buffer smaller than current capacity 2. Changed loop from 0..self.len() to 0..self.capacity() to ensure all nodes are copied, not just the count in use The storage layout can have gaps like [VALID | VALID | INVALID | VALID] where deleted nodes remain in the buffer. The loop must iterate over capacity to copy all nodes including invalid ones. ## How This Was Tested Added two tests: - test_resize_prevents_capacity_shrink(): Verifies assert prevents shrinking - test_resize_copies_all_nodes_including_gaps(): Verifies all nodes copied All 49 tests passing. Cargo clippy and fmt passing.
Per review feedback, renamed the resize() method to expand() to make it clear that this function cannot shrink the storage capacity - it only allows expansion. Updated all function names, tests, and added documentation to clarify this behavior. The expand() function now clearly indicates that it can only increase capacity, preventing confusion about whether the buffer can be shrunk.
4c35d80 to
05e11b2
Compare
Description
Fixed resize() to prevent capacity shrinking and properly copy all nodes during resizing.
The root issue was that resize() allowed shrinking to a buffer smaller than the current capacity. This caused out-of-bounds access when the resize loop tried to copy node pointers. The storage layout can have gaps like
[VALID | VALID | INVALID | VALID | INVALID]where deleted nodes remain in the buffer at their original indices.Changes:
buffer.len() >= self.len()tobuffer.len() >= self.capacity()to prevent resizing to a buffer smaller than current capacity0..self.len()to0..self.capacity()to ensure all nodes including gaps are copiedHow This Was Tested
cargo test --package patina_internal_collections)Integration Instructions