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