ab_client_database/
storage_backend.rs

1use futures::channel::oneshot;
2use std::mem::MaybeUninit;
3use std::{fmt, io, mem};
4
5/// A wrapper data structure with 4096 bytes alignment, which is the most common alignment for
6/// direct I/O operations.
7#[derive(Debug, Copy, Clone)]
8#[repr(C, align(4096))]
9pub struct AlignedPage([u8; AlignedPage::SIZE]);
10
11const _: () = {
12    assert!(align_of::<AlignedPage>() == AlignedPage::SIZE);
13};
14
15impl Default for AlignedPage {
16    #[inline(always)]
17    fn default() -> Self {
18        Self([0; AlignedPage::SIZE])
19    }
20}
21
22impl AlignedPage {
23    /// 4096 is as a relatively safe size due to sector size on SSDs commonly being 512 or 4096
24    /// bytes
25    pub const SIZE: usize = 4096;
26
27    /// Convert an exclusive slice to an uninitialized version
28    pub fn as_uninit_slice_mut(value: &mut [Self]) -> &mut [MaybeUninit<Self>] {
29        // SAFETY: Same layout
30        unsafe { mem::transmute(value) }
31    }
32
33    /// Convenient conversion from slice to underlying representation for efficiency purposes
34    #[inline(always)]
35    pub fn slice_to_repr(value: &[Self]) -> &[[u8; AlignedPage::SIZE]] {
36        // SAFETY: `AlignedPage` is `#[repr(C)]` and guaranteed to have the same memory layout
37        unsafe { mem::transmute(value) }
38    }
39
40    /// Convenient conversion from slice to underlying representation for efficiency purposes
41    #[inline(always)]
42    pub fn uninit_slice_to_repr(
43        value: &[MaybeUninit<Self>],
44    ) -> &[MaybeUninit<[u8; AlignedPage::SIZE]>] {
45        // SAFETY: `AlignedPage` is `#[repr(C)]` and guaranteed to have the same memory layout
46        unsafe { mem::transmute(value) }
47    }
48
49    /// Convenient conversion from a slice of underlying representation for efficiency purposes.
50    ///
51    /// Returns `None` if not correctly aligned.
52    #[inline]
53    pub fn try_slice_from_repr(value: &[[u8; AlignedPage::SIZE]]) -> Option<&[Self]> {
54        // SAFETY: All bit patterns are valid
55        let (before, slice, after) = unsafe { value.align_to::<Self>() };
56
57        if before.is_empty() && after.is_empty() {
58            Some(slice)
59        } else {
60            None
61        }
62    }
63
64    /// Convenient conversion from a slice of underlying representation for efficiency purposes.
65    ///
66    /// Returns `None` if not correctly aligned.
67    #[inline]
68    pub fn try_uninit_slice_from_repr(
69        value: &[MaybeUninit<[u8; AlignedPage::SIZE]>],
70    ) -> Option<&[MaybeUninit<Self>]> {
71        // SAFETY: All bit patterns are valid
72        let (before, slice, after) = unsafe { value.align_to::<MaybeUninit<Self>>() };
73
74        if before.is_empty() && after.is_empty() {
75            Some(slice)
76        } else {
77            None
78        }
79    }
80
81    /// Convenient conversion from mutable slice to underlying representation for efficiency
82    /// purposes
83    #[inline(always)]
84    pub fn slice_mut_to_repr(slice: &mut [Self]) -> &mut [[u8; AlignedPage::SIZE]] {
85        // SAFETY: `AlignedSectorSize` is `#[repr(C)]` and its alignment is larger than inner value
86        unsafe { mem::transmute(slice) }
87    }
88
89    /// Convenient conversion from mutable slice to underlying representation for efficiency
90    /// purposes
91    #[inline(always)]
92    pub fn uninit_slice_mut_to_repr(
93        slice: &mut [MaybeUninit<Self>],
94    ) -> &mut [MaybeUninit<[u8; AlignedPage::SIZE]>] {
95        // SAFETY: `AlignedSectorSize` is `#[repr(C)]` and its alignment is larger than inner value
96        unsafe { mem::transmute(slice) }
97    }
98
99    /// Convenient conversion from a slice of underlying representation for efficiency purposes.
100    ///
101    /// Returns `None` if not correctly aligned.
102    #[inline]
103    pub fn try_slice_mut_from_repr(value: &mut [[u8; AlignedPage::SIZE]]) -> Option<&mut [Self]> {
104        // SAFETY: All bit patterns are valid
105        let (before, slice, after) = unsafe { value.align_to_mut::<Self>() };
106
107        if before.is_empty() && after.is_empty() {
108            Some(slice)
109        } else {
110            None
111        }
112    }
113
114    /// Convenient conversion from a slice of underlying representation for efficiency purposes.
115    ///
116    /// Returns `None` if not correctly aligned.
117    #[inline]
118    pub fn try_uninit_slice_mut_from_repr(
119        value: &mut [MaybeUninit<[u8; AlignedPage::SIZE]>],
120    ) -> Option<&mut [MaybeUninit<Self>]> {
121        // SAFETY: All bit patterns are valid
122        let (before, slice, after) = unsafe { value.align_to_mut::<MaybeUninit<Self>>() };
123
124        if before.is_empty() && after.is_empty() {
125            Some(slice)
126        } else {
127            None
128        }
129    }
130}
131
132/// Storage backend to be used by [`ClientDatabase`]
133///
134/// [`ClientDatabase`]: crate::ClientDatabase
135pub trait ClientDatabaseStorageBackend: fmt::Debug + Send + Sync + 'static {
136    /// Total number of pages available for reads/writes
137    fn num_pages(&self) -> u32;
138
139    // TODO: Think whether `Vec` is the right wrapper here to avoid reallocations
140    /// Reading into aligned memory.
141    ///
142    /// `length` is the number of [`AlignedPage`] units (pages) to read into (append to)
143    /// `buffer`. `offset` is in pages too.
144    fn read(
145        &self,
146        buffer: Vec<AlignedPage>,
147        length: u32,
148        offset: u32,
149    ) -> oneshot::Receiver<io::Result<Vec<AlignedPage>>>;
150
151    // TODO: Think whether `Vec` is the right wrapper here to avoid reallocations
152    /// Writing from aligned memory.
153    ///
154    /// `offset` is in [`AlignedPage`] units (pages). After successful writing returns allocated
155    /// pages back to the caller.
156    fn write(
157        &self,
158        buffer: Vec<AlignedPage>,
159        offset: u32,
160    ) -> oneshot::Receiver<io::Result<Vec<AlignedPage>>>;
161}