Skip to content

Commit e70e26c

Browse files
committed
feat: page checksum
1 parent 4b29fc5 commit e70e26c

7 files changed

Lines changed: 90 additions & 43 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ serde = { version = "1.0", features = ["derive"] }
2525
bloomfilter = "1.0"
2626
fs2 = "0.4.3"
2727
bincode = "1.3.3"
28+
crc32fast = "1.4.2"
2829

2930
[build-dependencies]
3031
hooky-rs = "1.0.0"

src/btree/mod.rs

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -449,12 +449,22 @@ impl<
449449
}
450450
}
451451

452-
// #[cfg(test)]
453-
// mod btree_tests {
454-
// use super::*;
455-
456-
// #[test]
457-
// fn create_btree() {
458-
// let mut btree = BTree::<u32, String>::new("test_data/btree").unwrap();
459-
// }
460-
// }
452+
#[cfg(test)]
453+
mod btree_tests {
454+
use super::*;
455+
456+
#[test]
457+
fn create_btree() {
458+
let mut btree = BTree::<u32, u32>::new("test_data/btree").unwrap();
459+
460+
btree.insert(12, 12 * 2).unwrap();
461+
btree.insert(6, 6 * 2).unwrap();
462+
btree.insert(2, 2 * 2).unwrap();
463+
btree.insert(16, 16 * 2).unwrap();
464+
465+
assert_eq!(btree.values().unwrap(), vec![24, 12, 4, 32]);
466+
467+
let value = btree.get(&2).unwrap().unwrap();
468+
assert_eq!(value, 4)
469+
}
470+
}

src/collection/storage.rs

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -154,13 +154,7 @@ impl Storage {
154154
let mut file = fs::OpenOptions::new()
155155
.read(true)
156156
.open(self.storage_path.join(filename.clone()))
157-
.map_err(|r| match r.kind() {
158-
std::io::ErrorKind::NotFound => Error::CorruptedData(format!(
159-
"Data chunk {} not found, but index contains it",
160-
filename
161-
)),
162-
_ => Error::IoError(r),
163-
})?;
157+
.map_err(Error::IoError)?;
164158

165159
Ok(Some(Self::deserialize_value(&mut file, offset, &filename)?))
166160
}
@@ -200,12 +194,7 @@ impl Storage {
200194
let mut value = vec![0; length];
201195
file.read_exact(&mut value).map_err(Error::IoError)?;
202196

203-
let value = bincode::deserialize(&value).map_err(|e| {
204-
Error::CorruptedData(format!(
205-
"Corrupted data chunk {} and offset {}. Error: {}",
206-
filename, offset, e
207-
))
208-
})?;
197+
let value = bincode::deserialize(&value).map_err(Error::SerializeError)?;
209198

210199
Ok(value)
211200
}

src/collection/wal.rs

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -253,13 +253,7 @@ impl Wal {
253253
let mut file = fs::OpenOptions::new()
254254
.read(true)
255255
.open(self.config.data_path.join("log").join(&filename))
256-
.map_err(|r| match r.kind() {
257-
std::io::ErrorKind::NotFound => Error::CorruptedData(format!(
258-
"WAL Log {} not found, but wal index contains it",
259-
filename
260-
)),
261-
_ => Error::IoError(r),
262-
})?;
256+
.map_err(Error::IoError)?;
263257

264258
Self::deserialize_value(&mut file, offset, &filename)
265259
}
@@ -296,12 +290,7 @@ impl Wal {
296290
let mut value = vec![0; length];
297291
file.read_exact(&mut value).unwrap();
298292

299-
let value = bincode::deserialize(&value).map_err(|e| {
300-
Error::CorruptedData(format!(
301-
"Corrupted wal log {} and offset {}. Error: {}",
302-
filename, offset, e
303-
))
304-
})?;
293+
let value = bincode::deserialize(&value).map_err(Error::SerializeError)?;
305294

306295
Ok(Some(value))
307296
}

src/error.rs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ pub enum Error {
77
DatabaseLocked,
88
AlreadyExists(String),
99
NotFound(String),
10-
CorruptedData(String),
10+
CorruptedData(CorruptedDataError),
1111
Other(String),
1212
Cannot(String),
1313
}
@@ -21,7 +21,7 @@ impl Debug for Error {
2121
write!(f, "Database is locked, maybe another instance is running?")
2222
}
2323
Error::Other(err) => write!(f, "Other Error: {}", err),
24-
Error::CorruptedData(err) => write!(f, "Corrupted data: {}", err),
24+
Error::CorruptedData(err) => write!(f, "{:?}", err),
2525
Error::AlreadyExists(message) => write!(f, "{} already exists", message),
2626
Error::NotFound(message) => write!(f, "{} not found", message),
2727
Error::Cannot(message) => write!(f, "cannot {}", message),
@@ -30,4 +30,21 @@ impl Debug for Error {
3030
}
3131
}
3232

33+
pub struct CorruptedDataError {
34+
pub kind: CorruptedDataKind,
35+
pub message: String,
36+
}
37+
38+
#[derive(Debug)]
39+
pub enum CorruptedDataKind {
40+
ChecksumNotMatch,
41+
UnsyncWithWAL,
42+
}
43+
44+
impl Debug for CorruptedDataError {
45+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46+
write!(f, "Corrupted data: {}. {:?}", self.message, self.kind)
47+
}
48+
}
49+
3350
pub type Result<T> = std::result::Result<T, Error>;

src/page.rs

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ use std::{
55
};
66

77
use crate::{
8-
error::{Error, Result},
8+
error::{CorruptedDataError, CorruptedDataKind, Error, Result},
99
Either,
1010
};
11+
use crc32fast::Hasher;
1112
use layout::{CellPointerFlags, CellPointerMetadata, PageHeader};
1213
use serde::{de::DeserializeOwned, Serialize};
1314
use spec::{LocationOffset, CELL_POINTER_SIZE, PAGE_FREE_SPACE_BYTE, PAGE_HEADER_SIZE, PAGE_SIZE};
@@ -35,28 +36,45 @@ impl<T: Serialize + DeserializeOwned + PartialOrd + Ord + Clone> Page<T> {
3536
upper,
3637
lower,
3738
special,
39+
checksum: 0,
3840
};
3941

4042
let header_bytes = bincode::serialize(&header).map_err(Error::SerializeError)?;
4143
io.seek(SeekFrom::Start(0)).map_err(Error::IoError)?;
4244
io.write(&header_bytes).map_err(Error::IoError)?;
4345

44-
Ok(Page {
46+
let mut page: Page<T> = Page {
4547
header,
4648
io,
4749
_t: PhantomData,
48-
})
50+
};
51+
52+
// update checksum
53+
page.write_header()?;
54+
55+
Ok(page)
4956
}
5057

5158
pub fn open(data: [u8; PAGE_SIZE as usize]) -> Result<Self> {
5259
let mut io = Cursor::new(data);
5360
let header = Self::read_header(&mut io)?;
5461

55-
Ok(Self {
62+
let page = Self {
5663
io,
57-
header,
64+
header: header.clone(),
5865
_t: PhantomData,
59-
})
66+
};
67+
68+
let checksum = page.checksum();
69+
70+
if header.checksum != checksum {
71+
return Err(Error::CorruptedData(CorruptedDataError {
72+
kind: CorruptedDataKind::ChecksumNotMatch,
73+
message: "checksum does not match".to_string(),
74+
}));
75+
}
76+
77+
Ok(page)
6078
}
6179

6280
pub fn write(&mut self, data: T) -> Result<(LocationOffset, LocationOffset)> {
@@ -442,6 +460,9 @@ impl<T: Serialize + DeserializeOwned + PartialOrd + Ord + Clone> Page<T> {
442460
}
443461

444462
fn write_header(&mut self) -> Result<()> {
463+
let checksum = self.checksum();
464+
self.header.checksum = checksum;
465+
445466
let buffer = bincode::serialize(&self.header).map_err(Error::SerializeError)?;
446467

447468
self.io.seek(SeekFrom::Start(0)).map_err(Error::IoError)?;
@@ -458,6 +479,12 @@ impl<T: Serialize + DeserializeOwned + PartialOrd + Ord + Clone> Page<T> {
458479

459480
bincode::deserialize(&buffer).map_err(Error::SerializeError)
460481
}
482+
483+
fn checksum(&self) -> u32 {
484+
let mut hasher = Hasher::new();
485+
hasher.update(&self.io.get_ref()[PAGE_HEADER_SIZE..]);
486+
hasher.finalize()
487+
}
461488
}
462489

463490
#[cfg(test)]
@@ -589,4 +616,17 @@ mod page_tests {
589616

590617
assert_eq!(value, 90);
591618
}
619+
620+
#[test]
621+
fn page_checksum() {
622+
let mut page = Page::<u32>::create(0).unwrap();
623+
624+
page.write(99).unwrap();
625+
626+
let mut page_bytes = page.to_bytes().unwrap();
627+
//change a random byte
628+
page_bytes[26] = 2u8;
629+
630+
Page::<u32>::open(page_bytes).err().unwrap();
631+
}
592632
}

src/page/layout.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use serde::{Deserialize, Serialize};
33

44
#[derive(Serialize, Deserialize, Debug, Clone)]
55
pub struct PageHeader {
6+
pub checksum: u32,
67
pub lower: LocationOffset,
78
pub upper: LocationOffset,
89
pub special: LocationOffset,

0 commit comments

Comments
 (0)