redb_32bit/tree_store/page_store/
savepoint.rs1use crate::transaction_tracker::{SavepointId, TransactionId, TransactionTracker};
2use crate::tree_store::{Checksum, PageNumber, TransactionalMemory};
3use crate::{RedbValue, TypeName};
4use std::fmt::Debug;
5use std::mem::size_of;
6use std::sync::{Arc, Mutex};
7
8pub struct Savepoint {
25 version: u8,
26 id: SavepointId,
27 transaction_id: TransactionId,
30 user_root: Option<(PageNumber, Checksum)>,
31 system_root: Option<(PageNumber, Checksum)>,
33 freed_root: Option<(PageNumber, Checksum)>,
34 regional_allocators: Vec<Vec<u8>>,
35 transaction_tracker: Arc<Mutex<TransactionTracker>>,
36 ephemeral: bool,
37}
38
39impl Savepoint {
40 #[allow(clippy::too_many_arguments)]
41 pub(crate) fn new_ephemeral(
42 mem: &TransactionalMemory,
43 transaction_tracker: Arc<Mutex<TransactionTracker>>,
44 id: SavepointId,
45 transaction_id: TransactionId,
46 user_root: Option<(PageNumber, Checksum)>,
47 system_root: Option<(PageNumber, Checksum)>,
48 freed_root: Option<(PageNumber, Checksum)>,
49 regional_allocators: Vec<Vec<u8>>,
50 ) -> Self {
51 Self {
52 id,
53 transaction_id,
54 version: mem.get_version(),
55 user_root,
56 system_root,
57 freed_root,
58 regional_allocators,
59 transaction_tracker,
60 ephemeral: true,
61 }
62 }
63
64 pub(crate) fn get_version(&self) -> u8 {
65 self.version
66 }
67
68 pub(crate) fn get_id(&self) -> SavepointId {
69 self.id
70 }
71
72 pub(crate) fn get_transaction_id(&self) -> TransactionId {
73 self.transaction_id
74 }
75
76 pub(crate) fn get_user_root(&self) -> Option<(PageNumber, Checksum)> {
77 self.user_root
78 }
79
80 pub(crate) fn get_freed_root(&self) -> Option<(PageNumber, Checksum)> {
81 self.freed_root
82 }
83
84 pub(crate) fn get_regional_allocator_states(&self) -> &[Vec<u8>] {
85 &self.regional_allocators
86 }
87
88 pub(crate) fn db_address(&self) -> *const Mutex<TransactionTracker> {
89 self.transaction_tracker.as_ref() as *const _
90 }
91
92 pub(crate) fn set_persistent(&mut self) {
93 self.ephemeral = false;
94 }
95}
96
97impl Drop for Savepoint {
98 fn drop(&mut self) {
99 if self.ephemeral {
100 self.transaction_tracker
101 .lock()
102 .unwrap()
103 .deallocate_savepoint(self.get_id(), self.get_transaction_id());
104 }
105 }
106}
107
108#[derive(Debug)]
109pub(crate) enum SerializedSavepoint<'a> {
110 Ref(&'a [u8]),
111 Owned(Vec<u8>),
112}
113
114impl<'a> SerializedSavepoint<'a> {
115 pub(crate) fn from_savepoint(savepoint: &Savepoint) -> Self {
116 let mut result = vec![savepoint.version];
117 result.extend(savepoint.id.0.to_le_bytes());
118 result.extend(savepoint.transaction_id.raw_id().to_le_bytes());
119
120 if let Some((root, checksum)) = savepoint.user_root {
121 result.push(1);
122 result.extend(root.to_le_bytes());
123 result.extend(checksum.to_le_bytes());
124 } else {
125 result.push(0);
126 result.extend([0; PageNumber::serialized_size()]);
127 result.extend((0 as Checksum).to_le_bytes());
128 }
129
130 if let Some((root, checksum)) = savepoint.system_root {
131 result.push(1);
132 result.extend(root.to_le_bytes());
133 result.extend(checksum.to_le_bytes());
134 } else {
135 result.push(0);
136 result.extend([0; PageNumber::serialized_size()]);
137 result.extend((0 as Checksum).to_le_bytes());
138 }
139
140 if let Some((root, checksum)) = savepoint.freed_root {
141 result.push(1);
142 result.extend(root.to_le_bytes());
143 result.extend(checksum.to_le_bytes());
144 } else {
145 result.push(0);
146 result.extend([0; PageNumber::serialized_size()]);
147 result.extend((0 as Checksum).to_le_bytes());
148 }
149
150 result.extend(
151 u32::try_from(savepoint.regional_allocators.len())
152 .unwrap()
153 .to_le_bytes(),
154 );
155 for region in savepoint.regional_allocators.iter() {
156 assert_eq!(savepoint.regional_allocators[0].len(), region.len());
157 }
158 result.extend(
159 u32::try_from(savepoint.regional_allocators[0].len())
160 .unwrap()
161 .to_le_bytes(),
162 );
163
164 for region in savepoint.regional_allocators.iter() {
165 result.extend(region);
166 }
167 Self::Owned(result)
168 }
169
170 fn data(&self) -> &[u8] {
171 match self {
172 SerializedSavepoint::Ref(x) => x,
173 SerializedSavepoint::Owned(x) => x.as_slice(),
174 }
175 }
176
177 pub(crate) fn to_savepoint(
178 &self,
179 transaction_tracker: Arc<Mutex<TransactionTracker>>,
180 ) -> Savepoint {
181 let data = self.data();
182 let mut offset = 0;
183 let version = data[offset];
184 offset += size_of::<u8>();
185
186 let id = u64::from_le_bytes(
187 data[offset..(offset + size_of::<u64>())]
188 .try_into()
189 .unwrap(),
190 );
191 offset += size_of::<u64>();
192
193 let transaction_id = u64::from_le_bytes(
194 data[offset..(offset + size_of::<u64>())]
195 .try_into()
196 .unwrap(),
197 );
198 offset += size_of::<u64>();
199
200 let not_null = data[offset];
201 assert!(not_null == 0 || not_null == 1);
202 offset += 1;
203 let user_root = if not_null == 1 {
204 let page_number = PageNumber::from_le_bytes(
205 data[offset..(offset + PageNumber::serialized_size())]
206 .try_into()
207 .unwrap(),
208 );
209 offset += PageNumber::serialized_size();
210 let checksum = Checksum::from_le_bytes(
211 data[offset..(offset + size_of::<Checksum>())]
212 .try_into()
213 .unwrap(),
214 );
215 offset += size_of::<Checksum>();
216 Some((page_number, checksum))
217 } else {
218 offset += PageNumber::serialized_size();
219 offset += size_of::<Checksum>();
220 None
221 };
222
223 let not_null = data[offset];
224 assert!(not_null == 0 || not_null == 1);
225 offset += 1;
226 let system_root = if not_null == 1 {
227 let page_number = PageNumber::from_le_bytes(
228 data[offset..(offset + PageNumber::serialized_size())]
229 .try_into()
230 .unwrap(),
231 );
232 offset += PageNumber::serialized_size();
233 let checksum = Checksum::from_le_bytes(
234 data[offset..(offset + size_of::<Checksum>())]
235 .try_into()
236 .unwrap(),
237 );
238 offset += size_of::<Checksum>();
239 Some((page_number, checksum))
240 } else {
241 offset += PageNumber::serialized_size();
242 offset += size_of::<Checksum>();
243 None
244 };
245
246 let not_null = data[offset];
247 assert!(not_null == 0 || not_null == 1);
248 offset += 1;
249 let freed_root = if not_null == 1 {
250 let page_number = PageNumber::from_le_bytes(
251 data[offset..(offset + PageNumber::serialized_size())]
252 .try_into()
253 .unwrap(),
254 );
255 offset += PageNumber::serialized_size();
256 let checksum = Checksum::from_le_bytes(
257 data[offset..(offset + size_of::<Checksum>())]
258 .try_into()
259 .unwrap(),
260 );
261 offset += size_of::<Checksum>();
262 Some((page_number, checksum))
263 } else {
264 offset += PageNumber::serialized_size();
265 offset += size_of::<Checksum>();
266 None
267 };
268
269 let regions = u32::from_le_bytes(
270 data[offset..(offset + size_of::<u32>())]
271 .try_into()
272 .unwrap(),
273 ) as usize;
274 offset += size_of::<u32>();
275 let allocator_len = u32::from_le_bytes(
276 data[offset..(offset + size_of::<u32>())]
277 .try_into()
278 .unwrap(),
279 ) as usize;
280 offset += size_of::<u32>();
281
282 let mut regional_allocators = vec![];
283
284 for _ in 0..regions {
285 regional_allocators.push(data[offset..(offset + allocator_len)].to_vec());
286 offset += allocator_len;
287 }
288
289 assert_eq!(offset, data.len());
290
291 Savepoint {
292 version,
293 id: SavepointId(id),
294 transaction_id: TransactionId::new(transaction_id),
295 user_root,
296 system_root,
297 freed_root,
298 regional_allocators,
299 transaction_tracker,
300 ephemeral: false,
301 }
302 }
303}
304
305impl<'data> RedbValue for SerializedSavepoint<'data> {
306 type SelfType<'a> = SerializedSavepoint<'a> where Self: 'a;
307 type AsBytes<'a> = &'a [u8] where Self: 'a;
308
309 fn fixed_width() -> Option<usize> {
310 None
311 }
312
313 fn from_bytes<'a>(data: &'a [u8]) -> Self::SelfType<'a>
314 where
315 Self: 'a,
316 {
317 SerializedSavepoint::Ref(data)
318 }
319
320 fn as_bytes<'a, 'b: 'a>(value: &'a Self::SelfType<'b>) -> Self::AsBytes<'a>
321 where
322 Self: 'a,
323 Self: 'b,
324 {
325 value.data()
326 }
327
328 fn type_name() -> TypeName {
329 TypeName::internal("redb::SerializedSavepoint")
330 }
331}