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}