|
| 1 | +use std::cell::RefCell; |
| 2 | +use std::ops::{Deref, DerefMut}; |
| 3 | + |
| 4 | +/// A buffer that is returned to the free-list after usage. |
| 5 | +#[derive(Clone)] |
| 6 | +pub struct Buffer<'repo> { |
| 7 | + /// The buffer that would be returned to the freelist of `repo`. |
| 8 | + /// Note that buffers without capacity (i.e. without allocation) aren't returned. |
| 9 | + pub inner: Vec<u8>, |
| 10 | + /// The repository from whose free-list the `inner` buffer was taken, and to which it will be returned. |
| 11 | + pub repo: &'repo crate::Repository, |
| 12 | +} |
| 13 | + |
| 14 | +impl From<Buffer<'_>> for Vec<u8> { |
| 15 | + fn from(mut value: Buffer<'_>) -> Self { |
| 16 | + std::mem::take(&mut value.inner) |
| 17 | + } |
| 18 | +} |
| 19 | + |
| 20 | +impl Deref for Buffer<'_> { |
| 21 | + type Target = Vec<u8>; |
| 22 | + |
| 23 | + fn deref(&self) -> &Self::Target { |
| 24 | + &self.inner |
| 25 | + } |
| 26 | +} |
| 27 | + |
| 28 | +impl DerefMut for Buffer<'_> { |
| 29 | + fn deref_mut(&mut self) -> &mut Self::Target { |
| 30 | + &mut self.inner |
| 31 | + } |
| 32 | +} |
| 33 | + |
| 34 | +impl Drop for Buffer<'_> { |
| 35 | + fn drop(&mut self) { |
| 36 | + self.repo.reuse_buffer(&mut self.inner); |
| 37 | + } |
| 38 | +} |
| 39 | + |
| 40 | +/// Internal |
| 41 | +impl crate::Repository { |
| 42 | + /// Note that the returned buffer might still have data in it. |
| 43 | + #[inline] |
| 44 | + pub(crate) fn free_buf(&self) -> Vec<u8> { |
| 45 | + self.bufs |
| 46 | + .as_ref() |
| 47 | + .and_then(|bufs| bufs.borrow_mut().pop()) |
| 48 | + .unwrap_or_default() |
| 49 | + } |
| 50 | + |
| 51 | + /// This method is commonly called from the destructor of objects that previously claimed an entry |
| 52 | + /// in the free-list with [crate::Repository::free_buf]. |
| 53 | + /// They are welcome to take out the data themselves, for instance when the object is detached, to avoid |
| 54 | + /// it to be reclaimed. |
| 55 | + #[inline] |
| 56 | + pub(crate) fn reuse_buffer(&self, data: &mut Vec<u8>) { |
| 57 | + if data.capacity() > 0 { |
| 58 | + if let Some(bufs) = self.bufs.as_ref() { |
| 59 | + bufs.borrow_mut().push(std::mem::take(data)); |
| 60 | + } |
| 61 | + } |
| 62 | + } |
| 63 | +} |
| 64 | + |
| 65 | +/// Freelist configuration |
| 66 | +/// |
| 67 | +/// The free-list is an internal and 'transparent' mechanism for obtaining and re-using memory buffers when |
| 68 | +/// reading objects. That way, trashing is avoided as buffers are re-used and re-written. |
| 69 | +/// |
| 70 | +/// However, there are circumstances when releasing memory early is preferred, for instance on the server side. |
| 71 | +/// |
| 72 | +/// Also note that the free-list isn't cloned, so each clone of this instance starts with an empty one. |
| 73 | +impl crate::Repository { |
| 74 | + /// Return an empty buffer which is tied to this repository instance, and reuse its memory allocation by |
| 75 | + /// keeping it around even after it drops. |
| 76 | + pub fn empty_reusable_buffer(&self) -> Buffer<'_> { |
| 77 | + let mut inner = self.free_buf(); |
| 78 | + inner.clear(); |
| 79 | + Buffer { inner, repo: self } |
| 80 | + } |
| 81 | + |
| 82 | + /// Set the currently used freelist to `list`. If `None`, it will be disabled entirely. |
| 83 | + /// |
| 84 | + /// Return the currently previously allocated free-list, a list of reusable buffers typically used when reading objects. |
| 85 | + /// May be `None` if there was no free-list. |
| 86 | + pub fn set_freelist(&mut self, list: Option<Vec<Vec<u8>>>) -> Option<Vec<Vec<u8>>> { |
| 87 | + let previous = self.bufs.take(); |
| 88 | + self.bufs = list.map(RefCell::new); |
| 89 | + previous.map(RefCell::into_inner) |
| 90 | + } |
| 91 | + |
| 92 | + /// A builder method to disable the free-list on a newly created instance. |
| 93 | + pub fn without_freelist(mut self) -> Self { |
| 94 | + self.bufs.take(); |
| 95 | + self |
| 96 | + } |
| 97 | +} |
0 commit comments