Skip to main content

turbo_persistence/
arc_bytes.rs

1use std::{
2    borrow::Borrow,
3    fmt::{self, Debug, Formatter},
4    hash::{Hash, Hasher},
5    ops::{Deref, Range},
6    sync::Arc,
7};
8
9use memmap2::Mmap;
10
11use crate::{
12    compression::decompress_into_arc,
13    shared_bytes::{SharedBytes, is_subslice_of},
14};
15/// The backing storage for an `ArcBytes`.
16///
17/// The inner values are never read directly — they exist solely to keep the
18/// backing memory alive while the raw `data` pointer in `ArcBytes` references it.
19#[derive(Clone)]
20enum Backing {
21    Arc { _backing: Arc<[u8]> },
22    Mmap { _backing: Arc<Mmap> },
23}
24
25/// An owned byte slice backed by either an `Arc<[u8]>` or a memory-mapped file.
26#[derive(Clone)]
27pub struct ArcBytes {
28    data: *const [u8],
29    // Safety: Backing should come last so that it is dropped after the data pointer so we don't
30    // create a dangling pointer.  This isn't really a problem since it is technically ok to have
31    // dangling _pointers_.
32    backing: Backing,
33}
34
35unsafe impl Send for ArcBytes {}
36unsafe impl Sync for ArcBytes {}
37
38impl From<Arc<[u8]>> for ArcBytes {
39    fn from(arc: Arc<[u8]>) -> Self {
40        Self {
41            data: &*arc as *const [u8],
42            backing: Backing::Arc { _backing: arc },
43        }
44    }
45}
46
47impl From<Box<[u8]>> for ArcBytes {
48    fn from(b: Box<[u8]>) -> Self {
49        Self::from(Arc::from(b))
50    }
51}
52
53impl Deref for ArcBytes {
54    type Target = [u8];
55
56    fn deref(&self) -> &Self::Target {
57        unsafe { &*self.data }
58    }
59}
60
61impl Borrow<[u8]> for ArcBytes {
62    fn borrow(&self) -> &[u8] {
63        self
64    }
65}
66
67impl Hash for ArcBytes {
68    fn hash<H: Hasher>(&self, state: &mut H) {
69        self.deref().hash(state)
70    }
71}
72
73impl PartialEq for ArcBytes {
74    fn eq(&self, other: &Self) -> bool {
75        self.deref().eq(other.deref())
76    }
77}
78
79impl Debug for ArcBytes {
80    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
81        Debug::fmt(&**self, f)
82    }
83}
84
85impl Eq for ArcBytes {}
86
87impl ArcBytes {
88    /// Returns `true` if this `ArcBytes` is backed by a memory-mapped file.
89    pub fn is_mmap_backed(&self) -> bool {
90        matches!(self.backing, Backing::Mmap { .. })
91    }
92
93    /// Returns `true` if the backing `Arc` allocation is shared (i.e., there
94    /// are other `Arc` clones referencing the same data outside the cache).
95    /// Always returns `false` for mmap-backed bytes, since the mmap `Arc` is
96    /// shared across all slices from the same file and is not a useful signal.
97    pub fn is_shared_arc(&self) -> bool {
98        match &self.backing {
99            Backing::Arc { _backing } => Arc::strong_count(_backing) > 1,
100            Backing::Mmap { .. } => false,
101        }
102    }
103}
104
105impl SharedBytes for ArcBytes {
106    type MmapHandle = Arc<Mmap>;
107
108    fn slice(self, range: Range<usize>) -> Self {
109        let data = &*self;
110        let data = &data[range] as *const [u8];
111        Self {
112            data,
113            backing: self.backing,
114        }
115    }
116
117    unsafe fn slice_from_subslice(&self, subslice: &[u8]) -> Self {
118        debug_assert!(
119            is_subslice_of(
120                subslice,
121                match &self.backing {
122                    Backing::Arc { _backing } => _backing,
123                    Backing::Mmap { _backing } => _backing,
124                }
125            ),
126            "slice_from_subslice: subslice is not within the backing storage"
127        );
128        Self {
129            data: subslice as *const [u8],
130            backing: self.backing.clone(),
131        }
132    }
133
134    unsafe fn from_mmap(mmap: &Arc<Mmap>, subslice: &[u8]) -> Self {
135        debug_assert!(
136            is_subslice_of(subslice, mmap),
137            "from_mmap: subslice is not within the mmap"
138        );
139        ArcBytes {
140            data: subslice as *const [u8],
141            backing: Backing::Mmap {
142                _backing: mmap.clone(),
143            },
144        }
145    }
146
147    fn from_decompressed(uncompressed_length: u32, block: &[u8]) -> anyhow::Result<Self> {
148        Ok(ArcBytes::from(decompress_into_arc(
149            uncompressed_length,
150            block,
151        )?))
152    }
153}