-
Notifications
You must be signed in to change notification settings - Fork 7
Adds heap based CircularBufferAlloc variant that allows for runtime chosen capacity
#8
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: master
Are you sure you want to change the base?
Conversation
|
Thanks for submitting this. I like the way the documentation is built to avoid duplication. So... I know I said that I would be ok with using a macro to implement the two structs, however after thinking more carefully I think there's a better way: struct Backend<T, B>
where B: Deref<Target = [MaybeUninit<T>]> + DerefMut
{
size: usize,
start: usize,
items: B,
}
pub struct CircularBuffer<const N: usize, T> {
backend: Backend<T, [MaybeUninit<T>; T]>,
}
pub struct HeapCircularBuffer<T> {
backend: Backend<T, Box<[MaybeUninit<T>]>,
}With this approach, I believe that the iterator implementations could be easily shared. There would still be needed for two different sets of pub structs to avoid changing the type parameters. A macro could still be used to implement the frontend methods, in particular to avoid duplicating the documentation. Yet another approach could be to follow what the standard
I would keep This is the module structure I would use:
Some suggestions:
I would definitely want a shared implementation. Note that other trait and struct implementations will also get more complicated (see the various "TODO: optimize" entries to get an idea of the ones that will evolve in the near future). |
|
By the way, I noticed that the style of some code has changed, perhaps by a (partial) application of |
|
Thanks for the feedback! I can definitely remove the style changes. They originate indeed from rustfmt application of my ide. Because you do not want to use rustfmt right now. I would suggest to include the following in disable_all_formatting = trueThat way the code will not be automatically formatted until you decide to enable it again by removing the file. I like the idea of abstracting the into a common backend. I agree that it should allow us to reuse all the iterators if we can implement the necessary methods on But I think for this to work we need our own trait, because Maybe something like: pub(crate) trait AsSlice {
type Item;
fn as_slice(&self)-> &[Self::Item];
fn as_slice_mut(&mut self) -> &mut [Self::Item];
}I'll play with it a bit and see what i can come up with. |
36ed3ea to
fecc76f
Compare
|
I updated with the styling changes removed and generic backend based implementation. I do like this version a lot more than my initial implementation. It feel a lot cleaner and not so much of the implementation is hidden in macros. Also the sharing of the iterators works quite nicely, but they currently leak the backend storage type (and the private trait) in their public signature. I think we want to keep this an implementation detail, so we probably need some wrapper types to hide this. |
cad4e22 to
80e573f
Compare
|
Dropping a quick note to say that I haven't forgotten about this, but it might take some time before I have the capacity to look at it. This is a very busy month for me, sorry! |
|
No worries. Take the time you need. And thanks for the update. |
|
Another update: I started some work back in January to improve this branch even further and reduce a lot of code duplication. If I remember correctly, my changes are almost ready, there are just a few compile errors to fix. I'll try to find some time to complete those changes, push them on top of this branch (giving you credits for your work of course), and then merge them into master. It might take some more time but I'll do my best to get it done in a timely manner. |
This PR adds a variant of
CircularBufferthat is dynamically allocated on the heap calledHeapCircularBuffer. By allocating on the heap this comes without a const generic parameter and allows construction with a capacity determined at runtime instead of compile time. It is internally backed by aBox<[MaybeUninit<T>]>. This behaves very similar to the[MaybeUninit<T>; N]thats used byCircularBuffer, which means implementations can easily shared or adapted.The heap variant is located in the
heapmodule. The implementation is mostly shared by moving all common implementation s onto a genericBackendtype in thebackendmodule.CircularBufferandHeapCircularBufferare just wrappter type arount theBackend. The methods are simple forwarded to their respective implementation onBackendvia theimpl_buffermacro in the crate root.A similar method is used for the tests.
The borrowing iterators (
Iter,IterMut) can be shared as they are. The owning iterators (IntoIter,Drain) are also implemented around the genericBackend. This way their implementation is easily shared. To prevent leaking implementation details around theBackendtype, these are not exposed directly, but also using wrapper types.Most trait impls are also implemented on
Backendand the impl on the wrapper types simple forward to it.,All tests (including doc tests) passed on my machine (windows and wsl) and under miri (except for
randomized, but its the same formaster) ✅I think all the changes are fully backwards compatible (and
cargo-semver-checksdoes not flag anything)Open Questions:
IsCircularBufferAllocexpressive enough? Is there something better?HeapCircularBufferShouldShared via commonDrainshare impl details via macro, or is duplicating it fine?BackendIntoIterandDrainwrappers.Todos:
Possible Followups
allocinstead ofstdonlycloses #6