gix/repository/
freelist.rs

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