Skip to content

Commit 35460de

Browse files
authored
Merge pull request #2 from samuelorji/c2
chapter 2 done
2 parents 89cb0d8 + 7490f2f commit 35460de

File tree

6 files changed

+306
-32
lines changed

6 files changed

+306
-32
lines changed

Cargo.lock

+9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ edition = "2021"
66
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
77

88
[dependencies]
9+
byteorder = "1.4.3"

giraffe.db

8 KB
Binary file not shown.

src/dal.rs

+256-22
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,290 @@
11
use std::fs::File;
22
use std::os::unix::fs::{FileExt, OpenOptionsExt};
3-
3+
use std::path::Path;
4+
use crate::util::{Deserialize, Serialize};
5+
use byteorder::{ByteOrder, LittleEndian};
46
pub const PageSize : u32 = 4096;
7+
pub const META_PAGE_NUMBER: u16 = 0;
8+
pub const PAGE_SIZE: u16 = 4096;
9+
510
pub struct Dal {
611
file : File,
7-
pageSize : u32
12+
pageSize : u16,
13+
freeList: FreeList,
14+
meta: Meta
815
}
916

1017
impl Dal{
11-
pub fn new(path : &str, pageSize : u32) -> Self {
12-
let file = std::fs::OpenOptions::new()
13-
.create(true)
14-
.write(true)
15-
.append(true)
16-
.mode(0o666)
17-
.open(path)
18-
.unwrap();
19-
20-
Dal {
21-
file,
22-
pageSize
18+
pub fn new(path: &str) -> Self {
19+
if (!Path::new(path).exists()) {
20+
// database file doesn't exist, create a new database file
21+
let file = std::fs::OpenOptions::new()
22+
.create(true)
23+
.write(true)
24+
.read(true)
25+
.append(true)
26+
.mode(0o666)
27+
.open(path)
28+
.unwrap();
29+
30+
let mut freeList = FreeList::new();
31+
let mut meta = Meta::new();
32+
33+
let mut dal = Dal {
34+
file,
35+
pageSize: PAGE_SIZE, // PAGE_SIZE is 4KB (4096)
36+
meta,
37+
freeList,
38+
};
39+
40+
// set freelist to page 1 and increment maxPage in the freelist struct
41+
let freeListPageNumber = dal.freeList.getNextPageNumber();
42+
43+
dal.meta.freeListPage = freeListPageNumber;
44+
dal
45+
} else {
46+
// file exists, read meta to find freelist page and deserialize accordingly
47+
let file = std::fs::OpenOptions::new()
48+
.write(true)
49+
.read(true)
50+
.append(true)
51+
.open(path)
52+
.unwrap();
53+
54+
let mut freeList = FreeList::new();
55+
let mut meta = Meta::new();
56+
let mut dal = Dal {
57+
file,
58+
pageSize: PAGE_SIZE,
59+
meta,
60+
freeList,
61+
};
62+
63+
// read meta from disk and store in dal struct
64+
dal.readMeta();
65+
66+
// read freeList from and store in dal struct
67+
dal.readFreeList();
68+
dal
2369
}
2470
}
2571

26-
pub fn allocateEmptyPage(&self, pageNumber : Option<u64>) -> Page {
72+
pub fn allocateEmptyPage(&self, pageNumber : u16) -> Page {
2773
Page::new(pageNumber)
2874
}
2975

30-
fn readPage(&self, pageNumber : u64) -> Page {
31-
let mut page = self.allocateEmptyPage(Some(pageNumber));
32-
let offset = pageNumber * self.pageSize as u64;
76+
fn readPage(&self, pageNumber : u16) -> Page {
77+
let mut page = self.allocateEmptyPage(pageNumber);
78+
let offset = pageNumber as u64 * self.pageSize as u64;
3379
self.file.read_at(&mut page.data, offset).unwrap();
3480
page
3581
}
3682

3783
pub fn writePage(&mut self, page : &mut Page) {
38-
let offset = page.pageNumber * self.pageSize as u64;
84+
let offset = page.pageNumber as u64 * self.pageSize as u64;
3985
self.file.write_at(&page.data, offset).unwrap();
4086
}
4187

88+
pub fn writeMeta(&mut self) -> Page {
89+
// meta page is 0
90+
let mut page = self.allocateEmptyPage(META_PAGE_NUMBER); // META_PAGE_NUMBER = 0
91+
92+
// serialize the meta struct into the `page.data` buffer
93+
self.meta.serialize(&mut page.data[..]);
94+
95+
// write the page to disk
96+
self.writePage(&mut page);
97+
98+
page
99+
}
100+
101+
pub fn readMeta(&mut self) {
102+
// read page
103+
let page = self.readPage(META_PAGE_NUMBER); // META_PAGE_NUMBER = 0
104+
105+
// create an empty meta
106+
let mut meta = Meta::new();
107+
108+
// deserialize what's in the read `page.data` into the empty meta struct
109+
meta.deserialize(&page.data);
110+
self.meta = meta;
111+
}
112+
113+
pub fn writeFreeList(&mut self) -> Page {
114+
// get freelist page from meta
115+
let freeListPage = self.meta.freeListPage;
116+
117+
let mut page = self.allocateEmptyPage(freeListPage);
118+
119+
// serialize free list into the empty page
120+
self.freeList.serialize(&mut page.data);
121+
122+
// write page
123+
self.writePage(&mut page);
124+
page
125+
}
126+
127+
pub fn readFreeList(&mut self) {
128+
// get freelist page from meta
129+
let freeListPage = self.meta.freeListPage;
130+
131+
// read freelist page
132+
let mut page = self.readPage(freeListPage);
133+
134+
// deserialize free list page
135+
self.freeList.deserialize(&mut page.data);
136+
137+
}
138+
139+
pub fn getNextPageNumber(&mut self) -> u16 {
140+
self.freeList.getNextPageNumber()
141+
142+
}
143+
144+
pub fn releasePage(&mut self, pageNumber : u16) {
145+
self.freeList.releasePage(pageNumber);
146+
}
147+
}
148+
149+
impl Drop for Dal {
150+
fn drop(&mut self) {
151+
self.writeMeta();
152+
self.writeFreeList();
153+
}
42154
}
43155

44156
pub struct Page {
45-
pub pageNumber : u64,
157+
pub pageNumber : u16,
46158
pub data : Vec<u8>
47159
}
48160

49161
impl Page {
50-
pub fn new(pageNumber : Option<u64>) -> Self {
162+
pub fn new(pageNumber : u16) -> Self {
51163
Page {
52-
pageNumber: pageNumber.unwrap_or(0), // if no page number, set to 0
164+
pageNumber, // if no page number, set to 0
53165
data: vec![0_u8; PageSize as usize] // create a 4kB zero filled vector
54166
}
55167
}
168+
}
169+
170+
171+
#[derive(Debug)]
172+
struct FreeList {
173+
maxPage : u16, // Holds the maximum page allocated. maxPage * PageSize = databaseFileSize
174+
releasedPages: Vec<u16> // Pages that were previously allocated but are now free
175+
}
176+
177+
impl FreeList {
178+
pub fn new() -> Self {
179+
Self {
180+
maxPage : 0,
181+
releasedPages: vec![]
182+
}
183+
}
184+
185+
pub fn getNextPageNumber(&mut self) -> u16 {
186+
// if possible, get pages from released pages first, else increment maxPage and return it
187+
if let Some(releasedPageId) = self.releasedPages.pop() {
188+
releasedPageId
189+
} else {
190+
self.maxPage += 1 ;
191+
self.maxPage
192+
}
193+
}
194+
195+
pub fn releasePage(&mut self, pageNumber : u16) {
196+
self.releasedPages.push(pageNumber)
197+
}
198+
}
199+
200+
#[derive(Debug)]
201+
struct Meta {
202+
freeListPage : u16
203+
}
204+
205+
impl Meta {
206+
pub fn new() -> Self {
207+
Self {
208+
// start with a seed value of 0, this may change later on
209+
freeListPage : 0
210+
}
211+
}
212+
}
213+
214+
impl Serialize<Meta> for Meta {
215+
fn serialize(&self, buffer: &mut [u8]) {
216+
// To serialize a Meta
217+
// - 8 bytes for the freelist page
218+
let mut cursor: usize = 0;
219+
LittleEndian::write_u16(&mut buffer[cursor..], self.freeListPage);
220+
cursor += 8;
221+
}
222+
}
223+
224+
impl Deserialize<Meta> for Meta {
225+
fn deserialize(&mut self, buffer: &[u8]) {
226+
let mut cursor: usize = 0;
227+
// read 8 bytes for freelist cursor at cursor `cursor`
228+
let freeListPage = LittleEndian::read_u16(&buffer[cursor..]);
229+
cursor += 2;
230+
231+
self.freeListPage = freeListPage
232+
}
233+
}
234+
235+
impl Serialize<FreeList> for FreeList {
236+
fn serialize(&self, buffer: &mut [u8]) {
237+
238+
// To serialize a Meta
239+
// - 2 bytes for max page (u16)
240+
// - 2 bytes for length of released list (vector in our case)
241+
// - 2 bytes for each released page number
242+
243+
// write max page (2 bytes)
244+
let mut cursor: usize = 0;
245+
LittleEndian::write_u16(&mut buffer[cursor ..], self.maxPage);
246+
cursor += 2;
247+
248+
// write length of released pages (2 bytes)
249+
LittleEndian::write_u16(&mut buffer[cursor ..], self.releasedPages.len() as u16);
250+
cursor += 2;
251+
252+
// for each released page, write the released page number (2 bytes)
253+
for pgNumber in &self.releasedPages {
254+
LittleEndian::write_u16(&mut buffer[cursor ..], *pgNumber);
255+
cursor += 2;
256+
}
257+
}
258+
}
259+
260+
impl Deserialize<FreeList> for FreeList {
261+
fn deserialize(&mut self, buffer: &[u8]) {
262+
263+
// To deserialize a Meta
264+
// - 2 bytes for max page (u16)
265+
// - 2 bytes for length of released list (vector in our case)
266+
// - 2 bytes for each released page number
267+
268+
// read max page (2 bytes)
269+
let mut cursor : usize = 0;
270+
let maxPage = LittleEndian::read_u16(&buffer);
271+
cursor +=2;
272+
273+
// read length of released pages (2 bytes)
274+
let numReleasedPages = LittleEndian::read_u16(&buffer[cursor .. ]);
275+
cursor +=2;
276+
277+
let mut releasedPages : Vec<u16> = Vec::with_capacity(numReleasedPages as usize);
278+
279+
// for each released page, read the released page number (2 bytes)
280+
for _ in 0..numReleasedPages {
281+
let releasedPage = LittleEndian::read_u16(&buffer[cursor as usize ..]);
282+
releasedPages.push(releasedPage);
283+
cursor += 2
284+
285+
}
286+
287+
self.maxPage = maxPage ;
288+
self.releasedPages = releasedPages
289+
}
56290
}

src/main.rs

+33-10
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,50 @@
1+
use std::collections::VecDeque;
12
use crate::dal::Dal;
23

34
mod dal;
5+
mod util;
46

57
fn main() {
6-
// initialize Dal
7-
let mut dal = Dal::new("giraffe.db", dal::PageSize);
8-
9-
let mut page_0 = dal.allocateEmptyPage(Some(0));
108

11-
writeToPage(&mut page_0.data, "Page 0, hello", 0);
9+
// initialize Dal
10+
let mut dal = Dal::new("giraffe.db");
1211

13-
dal.writePage(&mut page_0);
12+
// create , write and commit page
13+
writeAndCommitPage(&mut dal, "first page");
14+
drop(dal);
1415

15-
let mut page_1 = dal.allocateEmptyPage(Some(1));
1616

17-
writeToPage(&mut page_1.data, "Page 1, hello", 0);
17+
// re initialize Dal
18+
let mut dal = Dal::new("giraffe.db");
19+
// create , write and commit page
20+
writeAndCommitPage(&mut dal, "second page");
21+
drop(dal);
1822

19-
// commit page
20-
dal.writePage(&mut page_1);
2123

24+
// re initialize Dal
25+
let mut dal = Dal::new("giraffe.db");
26+
let page_4 = dal.getNextPageNumber();
27+
dal.allocateEmptyPage(page_4);
28+
// release the page to test the free list
29+
dal.releasePage(page_4);
2230
}
2331

2432
fn writeToPage(buffer: &mut[u8],msg : &str, startAt: usize) {
2533
// write to buffer from position `startAt`
2634
buffer[startAt .. msg.len()].copy_from_slice(msg.as_bytes());
2735
}
36+
37+
fn writeAndCommitPage(dal : &mut Dal, data : &str ) {
38+
39+
//creates a page and writes the string to that page
40+
let nextPageNumber = dal.getNextPageNumber();
41+
42+
let mut page = dal.allocateEmptyPage(nextPageNumber);
43+
44+
// write some data to the created page
45+
writeToPage(&mut page.data, data, 0);
46+
47+
//persist data to disk
48+
dal.writePage(&mut page);
49+
50+
}

0 commit comments

Comments
 (0)