Skip to main content

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    /// Convenient conversion from slice to underlying representation for efficiency purposes
28    #[inline(always)]
29    pub fn slice_to_repr(value: &[Self]) -> &[[u8; AlignedPage::SIZE]] {
30        // SAFETY: `AlignedPage` is `#[repr(C)]` and guaranteed to have the same memory layout
31        unsafe { mem::transmute(value) }
32    }
33
34    /// Convenient conversion from slice to underlying representation for efficiency purposes
35    #[inline(always)]
36    pub fn uninit_slice_to_repr(
37        value: &[MaybeUninit<Self>],
38    ) -> &[MaybeUninit<[u8; AlignedPage::SIZE]>] {
39        // SAFETY: `AlignedPage` is `#[repr(C)]` and guaranteed to have the same memory layout
40        unsafe { mem::transmute(value) }
41    }
42
43    /// Convenient conversion from a slice of underlying representation for efficiency purposes.
44    ///
45    /// Returns `None` if not correctly aligned.
46    #[inline]
47    pub fn try_slice_from_repr(value: &[[u8; AlignedPage::SIZE]]) -> Option<&[Self]> {
48        // SAFETY: All bit patterns are valid
49        let (before, slice, after) = unsafe { value.align_to::<Self>() };
50
51        if before.is_empty() && after.is_empty() {
52            Some(slice)
53        } else {
54            None
55        }
56    }
57
58    /// Convenient conversion from a slice of underlying representation for efficiency purposes.
59    ///
60    /// Returns `None` if not correctly aligned.
61    #[inline]
62    pub fn try_uninit_slice_from_repr(
63        value: &[MaybeUninit<[u8; AlignedPage::SIZE]>],
64    ) -> Option<&[MaybeUninit<Self>]> {
65        // SAFETY: All bit patterns are valid
66        let (before, slice, after) = unsafe { value.align_to::<MaybeUninit<Self>>() };
67
68        if before.is_empty() && after.is_empty() {
69            Some(slice)
70        } else {
71            None
72        }
73    }
74
75    /// Convenient conversion from mutable slice to underlying representation for efficiency
76    /// purposes
77    #[inline(always)]
78    pub fn slice_mut_to_repr(slice: &mut [Self]) -> &mut [[u8; AlignedPage::SIZE]] {
79        // SAFETY: `AlignedPage` is `#[repr(C)]` with a single `[u8; AlignedPage::SIZE]` field and
80        // alignment equal to its size, so it has no padding and the same size as the inner value,
81        // whose alignment is smaller. Any bytes written through the result are valid `AlignedPage`.
82        unsafe { mem::transmute(slice) }
83    }
84
85    /// Convenient conversion from mutable slice to underlying representation for efficiency
86    /// purposes
87    #[inline(always)]
88    pub fn uninit_slice_mut_to_repr(
89        slice: &mut [MaybeUninit<Self>],
90    ) -> &mut [MaybeUninit<[u8; AlignedPage::SIZE]>] {
91        // SAFETY: `AlignedPage` is `#[repr(C)]` with a single `[u8; AlignedPage::SIZE]` field and
92        // alignment equal to its size, so it has no padding and the same size as the inner value,
93        // whose alignment is smaller. `MaybeUninit` preserves the size and alignment of the wrapped
94        // type, and any bytes (initialized or not) written through the result are a valid
95        // `MaybeUninit<AlignedPage>`.
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}