Skip to main content

reifydb_core/
wal.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use reifydb_codec::{key::encoded::EncodedKey, row::bytes::EncodedBytes};
5use reifydb_value::util::cowvec::CowVec;
6use reifydb_wal::{
7	body::Body,
8	error::{Result, WalError},
9};
10
11use crate::{
12	common::CommitVersion,
13	delta::{Delta, RemoveAnnounce, SetPre},
14	key::any::TaggedKey,
15};
16
17const OP_SET: u8 = 0;
18const OP_REMOVE: u8 = 1;
19
20const PRE_ABSENT: u8 = 0;
21const PRE_PRESENT: u8 = 1;
22
23const ANNOUNCE_SILENT: u8 = 0;
24const ANNOUNCE_ANNOUNCED: u8 = 1;
25const ANNOUNCE_UNOBSERVED: u8 = 2;
26
27#[derive(Debug, Clone, PartialEq, Eq)]
28pub struct CommitRecord {
29	pub version: CommitVersion,
30	pub deltas: Vec<Delta>,
31}
32
33impl CommitRecord {
34	pub const KIND: u32 = 1;
35
36	pub fn stamp_version(encoded: &mut [u8], version: CommitVersion) {
37		encoded[..8].copy_from_slice(&version.0.to_le_bytes());
38	}
39
40	pub fn encode_refs(version: CommitVersion, deltas: &[&Delta], out: &mut Vec<u8>) {
41		out.extend_from_slice(&version.0.to_le_bytes());
42		put_len(out, deltas.len());
43		for delta in deltas {
44			match delta {
45				Delta::Set {
46					key,
47					bytes,
48					pre,
49				} => {
50					out.push(OP_SET);
51					put_bytes(out, key.encode().as_bytes());
52					put_bytes(out, bytes.as_ref());
53					match pre {
54						SetPre::Absent => out.push(PRE_ABSENT),
55						SetPre::Present(pre) => {
56							out.push(PRE_PRESENT);
57							put_bytes(out, pre.as_ref());
58						}
59						SetPre::Unknown => panic!(
60							"commit {} has an unresolved pre for key {:?}",
61							version.0, key
62						),
63					}
64				}
65				Delta::Remove {
66					key,
67					announce,
68				} => {
69					out.push(OP_REMOVE);
70					put_bytes(out, key.encode().as_bytes());
71					match announce {
72						RemoveAnnounce::Silent => out.push(ANNOUNCE_SILENT),
73						RemoveAnnounce::Announced {
74							pre,
75						} => {
76							out.push(ANNOUNCE_ANNOUNCED);
77							put_bytes(out, pre.as_ref());
78						}
79						RemoveAnnounce::Unobserved {
80							pre,
81						} => {
82							out.push(ANNOUNCE_UNOBSERVED);
83							put_bytes(out, pre.as_ref());
84						}
85					}
86				}
87			}
88		}
89	}
90}
91
92impl Body for CommitRecord {
93	fn kind(&self) -> u32 {
94		Self::KIND
95	}
96
97	fn encode(&self, out: &mut Vec<u8>) {
98		Self::encode_refs(self.version, &self.deltas.iter().collect::<Vec<_>>(), out);
99	}
100
101	fn decode(kind: u32, bytes: &[u8]) -> Result<Self> {
102		if kind != Self::KIND {
103			return Err(WalError::Body(format!("unknown commit record kind {kind}")));
104		}
105		let mut reader = Reader {
106			bytes,
107			at: 0,
108		};
109		let version = CommitVersion(reader.u64()?);
110		let count = reader.u32()?;
111		let mut deltas = Vec::new();
112		for _ in 0..count {
113			deltas.push(reader.delta()?);
114		}
115		if reader.at != bytes.len() {
116			return Err(WalError::Body(format!(
117				"{} trailing bytes after delta {count}",
118				bytes.len() - reader.at
119			)));
120		}
121		Ok(Self {
122			version,
123			deltas,
124		})
125	}
126}
127
128#[derive(Debug, Clone, PartialEq, Eq)]
129pub struct SingleRecord {
130	pub deltas: Vec<Delta>,
131}
132
133impl SingleRecord {
134	pub const KIND: u32 = 2;
135
136	pub fn encode_deltas(deltas: &[Delta], out: &mut Vec<u8>) {
137		put_len(out, deltas.len());
138		for delta in deltas {
139			match delta {
140				Delta::Set {
141					key,
142					bytes,
143					..
144				} => {
145					out.push(OP_SET);
146					put_bytes(out, key.encode().as_bytes());
147					put_bytes(out, bytes.as_ref());
148				}
149				Delta::Remove {
150					key,
151					..
152				} => {
153					out.push(OP_REMOVE);
154					put_bytes(out, key.encode().as_bytes());
155				}
156			}
157		}
158	}
159}
160
161impl Body for SingleRecord {
162	fn kind(&self) -> u32 {
163		Self::KIND
164	}
165
166	fn encode(&self, out: &mut Vec<u8>) {
167		Self::encode_deltas(&self.deltas, out);
168	}
169
170	fn decode(kind: u32, bytes: &[u8]) -> Result<Self> {
171		if kind != Self::KIND {
172			return Err(WalError::Body(format!("unknown single record kind {kind}")));
173		}
174		let mut reader = Reader {
175			bytes,
176			at: 0,
177		};
178		let count = reader.u32()?;
179		let mut deltas = Vec::new();
180		for _ in 0..count {
181			deltas.push(reader.single_delta()?);
182		}
183		if reader.at != bytes.len() {
184			return Err(WalError::Body(format!(
185				"{} trailing bytes after single delta {count}",
186				bytes.len() - reader.at
187			)));
188		}
189		Ok(Self {
190			deltas,
191		})
192	}
193}
194
195#[derive(Debug, Clone, PartialEq, Eq)]
196pub enum WalRecord {
197	Multi(CommitRecord),
198	Single(SingleRecord),
199}
200
201impl Body for WalRecord {
202	fn kind(&self) -> u32 {
203		match self {
204			Self::Multi(record) => record.kind(),
205			Self::Single(record) => record.kind(),
206		}
207	}
208
209	fn encode(&self, out: &mut Vec<u8>) {
210		match self {
211			Self::Multi(record) => record.encode(out),
212			Self::Single(record) => record.encode(out),
213		}
214	}
215
216	fn decode(kind: u32, bytes: &[u8]) -> Result<Self> {
217		match kind {
218			CommitRecord::KIND => Ok(Self::Multi(CommitRecord::decode(kind, bytes)?)),
219			SingleRecord::KIND => Ok(Self::Single(SingleRecord::decode(kind, bytes)?)),
220			kind => Err(WalError::Body(format!("unknown wal record kind {kind}"))),
221		}
222	}
223}
224
225fn put_len(out: &mut Vec<u8>, len: usize) {
226	let len = u32::try_from(len).unwrap_or_else(|_| panic!("commit record length {len} does not fit in u32"));
227	out.extend_from_slice(&len.to_le_bytes());
228}
229
230fn put_bytes(out: &mut Vec<u8>, bytes: &[u8]) {
231	put_len(out, bytes.len());
232	out.extend_from_slice(bytes);
233}
234
235struct Reader<'a> {
236	bytes: &'a [u8],
237	at: usize,
238}
239
240impl<'a> Reader<'a> {
241	fn take(&mut self, len: usize) -> Result<&'a [u8]> {
242		let end = self.at.checked_add(len).filter(|end| *end <= self.bytes.len()).ok_or_else(|| {
243			WalError::Body(format!(
244				"truncated at byte {}: needs {len} of {} remaining",
245				self.at,
246				self.bytes.len() - self.at
247			))
248		})?;
249		let slice = &self.bytes[self.at..end];
250		self.at = end;
251		Ok(slice)
252	}
253
254	fn u8(&mut self) -> Result<u8> {
255		Ok(self.take(1)?[0])
256	}
257
258	fn u32(&mut self) -> Result<u32> {
259		let mut buf = [0u8; 4];
260		buf.copy_from_slice(self.take(4)?);
261		Ok(u32::from_le_bytes(buf))
262	}
263
264	fn u64(&mut self) -> Result<u64> {
265		let mut buf = [0u8; 8];
266		buf.copy_from_slice(self.take(8)?);
267		Ok(u64::from_le_bytes(buf))
268	}
269
270	fn bytes(&mut self) -> Result<&'a [u8]> {
271		let len = self.u32()? as usize;
272		self.take(len)
273	}
274
275	fn encoded(&mut self) -> Result<EncodedBytes> {
276		Ok(EncodedBytes(CowVec::new(self.bytes()?.to_vec())))
277	}
278
279	fn key(&mut self) -> Result<TaggedKey> {
280		let at = self.at;
281		let key = EncodedKey::new(self.bytes()?);
282		TaggedKey::decode(&key).ok_or_else(|| WalError::Body(format!("undecodable key at byte {at}")))
283	}
284
285	fn delta(&mut self) -> Result<Delta> {
286		let at = self.at;
287		match self.u8()? {
288			OP_SET => {
289				let key = self.key()?;
290				let bytes = self.encoded()?;
291				let pre_at = self.at;
292				let pre = match self.u8()? {
293					PRE_ABSENT => SetPre::Absent,
294					PRE_PRESENT => SetPre::Present(self.encoded()?),
295					tag => {
296						return Err(WalError::Body(format!(
297							"unknown pre tag {tag} at byte {pre_at}"
298						)));
299					}
300				};
301				Ok(Delta::Set {
302					key,
303					bytes,
304					pre,
305				})
306			}
307			OP_REMOVE => {
308				let key = self.key()?;
309				let announce_at = self.at;
310				let announce = match self.u8()? {
311					ANNOUNCE_SILENT => RemoveAnnounce::Silent,
312					ANNOUNCE_ANNOUNCED => RemoveAnnounce::Announced {
313						pre: self.encoded()?,
314					},
315					ANNOUNCE_UNOBSERVED => RemoveAnnounce::Unobserved {
316						pre: self.encoded()?,
317					},
318					tag => {
319						return Err(WalError::Body(format!(
320							"unknown announce tag {tag} at byte {announce_at}"
321						)));
322					}
323				};
324				Ok(Delta::Remove {
325					key,
326					announce,
327				})
328			}
329			op => Err(WalError::Body(format!("unknown delta op {op} at byte {at}"))),
330		}
331	}
332
333	fn single_delta(&mut self) -> Result<Delta> {
334		let at = self.at;
335		match self.u8()? {
336			OP_SET => {
337				let key = self.key()?;
338				let bytes = self.encoded()?;
339				Ok(Delta::Set {
340					key,
341					bytes,
342					pre: SetPre::Unknown,
343				})
344			}
345			OP_REMOVE => Ok(Delta::remove_silent(self.key()?)),
346			op => Err(WalError::Body(format!("unknown single delta op {op} at byte {at}"))),
347		}
348	}
349}