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}