Skip to main content

turbo_persistence/
arc_bytes.rs

1use std::{
2    borrow::Borrow,
3    fmt::{self, Debug, Formatter},
4    hash::{Hash, Hasher},
5    io::{self, Read},
6    ops::{Deref, Range},
7    sync::Arc,
8};
9
10use memmap2::Mmap;
11
12/// The backing storage for an `ArcBytes`.
13///
14/// The inner values are never read directly — they exist solely to keep the
15/// backing memory alive while the raw `data` pointer in `ArcBytes` references it.
16#[derive(Clone)]
17enum Backing {
18    Arc { _backing: Arc<[u8]> },
19    Mmap { _backing: Arc<Mmap> },
20}
21
22/// An owned byte slice backed by either an `Arc<[u8]>` or a memory-mapped file.
23#[derive(Clone)]
24pub struct ArcBytes {
25    data: *const [u8],
26    backing: Backing,
27}
28
29unsafe impl Send for ArcBytes {}
30unsafe impl Sync for ArcBytes {}
31
32impl From<Arc<[u8]>> for ArcBytes {
33    fn from(arc: Arc<[u8]>) -> Self {
34        Self {
35            data: &*arc as *const [u8],
36            backing: Backing::Arc { _backing: arc },
37        }
38    }
39}
40
41impl From<Box<[u8]>> for ArcBytes {
42    fn from(b: Box<[u8]>) -> Self {
43        Self::from(Arc::from(b))
44    }
45}
46
47impl Deref for ArcBytes {
48    type Target = [u8];
49
50    fn deref(&self) -> &Self::Target {
51        unsafe { &*self.data }
52    }
53}
54
55impl Borrow<[u8]> for ArcBytes {
56    fn borrow(&self) -> &[u8] {
57        self
58    }
59}
60
61impl Hash for ArcBytes {
62    fn hash<H: Hasher>(&self, state: &mut H) {
63        self.deref().hash(state)
64    }
65}
66
67impl PartialEq for ArcBytes {
68    fn eq(&self, other: &Self) -> bool {
69        self.deref().eq(other.deref())
70    }
71}
72
73impl Debug for ArcBytes {
74    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
75        Debug::fmt(&**self, f)
76    }
77}
78
79impl Eq for ArcBytes {}
80
81impl Read for ArcBytes {
82    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
83        let available = &**self;
84        let len = std::cmp::min(buf.len(), available.len());
85        buf[..len].copy_from_slice(&available[..len]);
86        // Advance the slice view
87        self.data = &available[len..] as *const [u8];
88        Ok(len)
89    }
90}
91
92/// Returns `true` if `subslice` lies entirely within `backing`.
93fn is_subslice_of(subslice: &[u8], backing: &[u8]) -> bool {
94    let backing = backing.as_ptr_range();
95    let sub = subslice.as_ptr_range();
96    sub.start >= backing.start && sub.end <= backing.end
97}
98
99impl ArcBytes {
100    /// Returns a new `ArcBytes` that points to a sub-range of the current slice.
101    pub fn slice(self, range: Range<usize>) -> ArcBytes {
102        let data = &*self;
103        let data = &data[range] as *const [u8];
104        Self {
105            data,
106            backing: self.backing,
107        }
108    }
109
110    /// Creates a sub-slice from a slice reference that points into this ArcBytes' backing data.
111    ///
112    /// # Safety
113    ///
114    /// The caller must ensure that `subslice` points to memory within this ArcBytes'
115    /// backing storage (not just within the current slice view, but anywhere in the original
116    /// backing data).
117    pub unsafe fn slice_from_subslice(&self, subslice: &[u8]) -> ArcBytes {
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    /// Creates an `ArcBytes` backed by a memory-mapped file.
135    ///
136    /// # Safety
137    ///
138    /// The caller must ensure that `subslice` points to memory within the given `mmap`.
139    pub unsafe fn from_mmap(mmap: Arc<Mmap>, subslice: &[u8]) -> ArcBytes {
140        debug_assert!(
141            is_subslice_of(subslice, &mmap),
142            "from_mmap: subslice is not within the mmap"
143        );
144        ArcBytes {
145            data: subslice as *const [u8],
146            backing: Backing::Mmap { _backing: mmap },
147        }
148    }
149
150    /// Returns `true` if this `ArcBytes` is backed by a memory-mapped file.
151    pub fn is_mmap_backed(&self) -> bool {
152        matches!(self.backing, Backing::Mmap { .. })
153    }
154}