From 63f4dce705ee4240b4bf89894fc508e30d78fcec Mon Sep 17 00:00:00 2001 From: kjuulh Date: Tue, 29 Mar 2022 22:11:49 +0200 Subject: [PATCH] Add basic memory --- Cargo.lock | 46 +++++++++++++++++++++++ Cargo.toml | 2 + src/main.rs | 31 +++++++++++----- src/page.rs | 28 ++++++++++++++ src/pager.rs | 35 ++++++++++++++++++ src/row.rs | 84 ++++++++++++++++++++++++++++++++++++++++++ src/statement.rs | 95 +++++++++++++++++------------------------------- src/table.rs | 61 +++++++++++++++++++++++++++++++ 8 files changed, 311 insertions(+), 71 deletions(-) create mode 100644 src/page.rs create mode 100644 src/pager.rs create mode 100644 src/row.rs create mode 100644 src/table.rs diff --git a/Cargo.lock b/Cargo.lock index 67baf94..8a45219 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,24 @@ dependencies = [ "memchr", ] +[[package]] +name = "bincode" +version = "2.0.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f609ceb2c41b0d0277314a789ef0e7eb14593d5485f7c67320bed3924ebb1b33" +dependencies = [ + "bincode_derive", +] + +[[package]] +name = "bincode_derive" +version = "2.0.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "913287a8f3e00db4c7ae1b87e9b9b8cebd6b89217eaadfc281fa5c897da35dc3" +dependencies = [ + "virtue", +] + [[package]] name = "const_format" version = "0.2.22" @@ -78,10 +96,32 @@ version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +[[package]] +name = "serde" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "sqlite_clone" version = "0.1.0" dependencies = [ + "bincode", + "serde", "sscanf", ] @@ -125,3 +165,9 @@ name = "unicode-xid" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "virtue" +version = "0.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "757cfbfe0d17ee6f22fe97e536d463047d451b47cf9d11e2b7d1398b0ef274dd" diff --git a/Cargo.toml b/Cargo.toml index 352ae23..c9ec48a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,3 +7,5 @@ edition = "2021" [dependencies] sscanf = {version="0.2.1"} +bincode = "2.0.0-rc.1" +serde = {version="1.0.136", features=["derive"]} diff --git a/src/main.rs b/src/main.rs index 85cfba8..256614d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,18 +1,25 @@ +mod page; +mod pager; +mod row; mod statement; +mod table; +use crate::statement::ExecuteResult; +use crate::table::Table; use std::io; use std::io::Write; use std::process::exit; struct InputBuffer { buffer: Option, + table: Table, } - impl InputBuffer { pub fn new() -> Self { return Self { - buffer: None + buffer: None, + table: Table::new(), }; } @@ -34,11 +41,11 @@ impl InputBuffer { Err(String::from("could not handle input")) } } - _ => { Err(String::from("Could not initialize buffer")) } + _ => Err(String::from("Could not initialize buffer")), } } - pub(crate) fn parse(&self) { + pub(crate) fn parse(&mut self) { match &self.buffer { Some(command) => { if command.starts_with(".") { @@ -46,7 +53,15 @@ impl InputBuffer { } else { match Self::prepare_statement(&command.replace("\n", "")) { Ok(statement) => { - statement.execute() + let execution_result = statement.execute(&mut self.table); + match execution_result { + ExecuteResult::Success => { + println!("success") + } + _ => { + println!("failure") + } + } } Err(e) => { println!("{}", e); @@ -74,14 +89,10 @@ impl InputBuffer { } } - fn main() -> Result<(), String> { let mut input_buffer = InputBuffer::new(); loop { - input_buffer - .print_prompt() - .read_input()? - .parse() + input_buffer.print_prompt().read_input()?.parse() } } diff --git a/src/page.rs b/src/page.rs new file mode 100644 index 0000000..a17627b --- /dev/null +++ b/src/page.rs @@ -0,0 +1,28 @@ +use std::ops::{Deref, DerefMut}; + +#[derive(Debug, Clone)] +pub struct Page { + buffer: Vec, +} + +impl Page { + pub fn new(buffer: Vec) -> Self { + Self { + buffer, + } + } +} + +impl Deref for Page { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.buffer + } +} + +impl DerefMut for Page { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.buffer + } +} diff --git a/src/pager.rs b/src/pager.rs new file mode 100644 index 0000000..721b24e --- /dev/null +++ b/src/pager.rs @@ -0,0 +1,35 @@ +use crate::page::Page; + +pub struct Pager { + pages: Vec>, + page_size: usize, +} + +impl Pager { + pub fn new(page_size: usize) -> Self { + Self { + page_size, + pages: Vec::new(), + } + } + + pub fn get_mut_or_alloc(&mut self, page_num: usize) -> &mut Page { + if page_num >= self.pages.len() { + self.grow_pages(page_num) + } + + if self.pages[page_num].is_none() { + let buf = vec![0; self.page_size]; + let page = Page::new(buf); + self.pages[page_num] = Some(page) + } + + self.pages[page_num].as_mut().unwrap() + } + + #[inline] + fn grow_pages(&mut self, index: usize) { + println!("debug: Growing pages"); + self.pages.resize(index + 1, None) + } +} diff --git a/src/row.rs b/src/row.rs new file mode 100644 index 0000000..3f41668 --- /dev/null +++ b/src/row.rs @@ -0,0 +1,84 @@ +use std::{iter, slice}; +use std::io::Write; + +use bincode::{Decode, Encode}; +use bincode::config::Configuration; +use bincode::error::{DecodeError, EncodeError}; + +const COLUMN_USERNAME_SIZE: usize = 32; +const COLUMN_EMAIL_SIZE: usize = 255; + +#[derive(Encode, Decode)] +pub struct Row { + pub id: u32, + pub username: [u8; COLUMN_USERNAME_SIZE], + pub email: [u8; COLUMN_EMAIL_SIZE], +} + +pub trait Serialize { + fn serialize(&self) -> Result, EncodeError>; + fn deserialize(buffer: &[u8]) -> Result + where + Self: Sized; +} + +impl Serialize for Row { + fn serialize(&self) -> Result, EncodeError> { + bincode::encode_to_vec(self, bincode::config::standard()) + } + + fn deserialize(buffer: &[u8]) -> Result { + match bincode::decode_from_slice::(buffer, bincode::config::standard()) + { + Ok((row, _)) => Ok(row), + Err(e) => Err(e), + } + } +} + +impl Row { + pub fn new(id: u32, username: String, email: String) -> Result { + let username_bytes = username.as_bytes(); + let email_bytes = email.as_bytes(); + if username_bytes.len() > COLUMN_USERNAME_SIZE { + Err(String::from("username is too long")) + } else if email_bytes.len() > COLUMN_EMAIL_SIZE { + Err(String::from("email is too long")) + } else { + let username_buffer = &mut [0; COLUMN_USERNAME_SIZE]; + if let Err(_) = pad_buffer(username_buffer, username_bytes) { + panic!("could not pad username buffer") + } + let email_buffer = &mut [0; COLUMN_EMAIL_SIZE]; + if let Err(_) = pad_buffer(email_buffer, username_bytes) { + panic!("could not pad username buffer") + } + Ok(Self { + id, + username: *username_buffer, + email: *email_buffer, + }) + } + } + + + pub(crate) fn print(&self) { + println!("({}, {}, {})", self.id, String::from_utf8(self.email.to_vec()).unwrap(), String::from_utf8(self.username.to_vec()).unwrap()) + } +} + +fn pad_buffer(mut bytes: &mut [u8], source: &[u8]) -> std::io::Result { + bytes.write(source) +} + +pub struct RowIter<'a> { + inner: slice::Iter<'a, Row>, +} + +impl<'a> iter::Iterator for RowIter<'a> { + type Item = &'a Row; + + fn next(&mut self) -> Option { + self.inner.next() + } +} diff --git a/src/statement.rs b/src/statement.rs index 2903eb6..820133e 100644 --- a/src/statement.rs +++ b/src/statement.rs @@ -1,50 +1,16 @@ -use std::io::Write; -use std::mem::size_of; +use crate::row::Serialize; +use crate::table::Table; +use crate::{row}; + pub enum StatementType { - Insert { - row: Row - }, + Insert { row: row::Row }, Select, } -const COLUMN_USERNAME_SIZE: usize = 32; -const COLUMN_EMAIL_SIZE: usize = 255; - -pub struct Row { - id: u32, - username: [u8; COLUMN_USERNAME_SIZE], - email: [u8; COLUMN_EMAIL_SIZE], -} - -impl Row { - fn new(id: u32, username: String, email: String) -> Result { - let username_bytes = username.as_bytes(); - let email_bytes = email.as_bytes(); - if username_bytes.len() > COLUMN_USERNAME_SIZE { - Err(String::from("username is too long")) - } else if email_bytes.len() > COLUMN_EMAIL_SIZE { - Err(String::from("email is too long")) - } else { - let username_buffer = &mut [0; COLUMN_USERNAME_SIZE]; - if let Err(_) = pad_buffer(username_buffer, username_bytes) { - panic!("could not pad username buffer") - } - let email_buffer = &mut [0; COLUMN_EMAIL_SIZE]; - if let Err(_) = pad_buffer(email_buffer, username_bytes) { - panic!("could not pad username buffer") - } - Ok(Self { - id, - username: *username_buffer, - email: *email_buffer, - }) - } - } -} - -fn pad_buffer(mut bytes: &mut [u8], source: &[u8]) -> std::io::Result { - bytes.write(source) +pub enum ExecuteResult { + Error { error: String }, + Success, } pub struct Statement { @@ -53,43 +19,50 @@ pub struct Statement { impl Statement { pub fn new(statement_type: StatementType) -> Self { - Self { - statement_type, - } + Self { statement_type } } pub fn parse_statement(command: &String) -> Result { if command.starts_with("insert") { match sscanf::scanf!(command, "insert {} {} {}", usize, str, str) { Ok((id, username, email)) => { - match Row::new(u32::try_from(id).unwrap(), username.to_string(), email.to_string()) { - Ok(row) => { - Ok(Statement::new(StatementType::Insert { row: row })) - } - Err(e) => { Err(e) } + match row::Row::new( + u32::try_from(id).unwrap(), + username.to_string(), + email.to_string(), + ) { + Ok(row) => Ok(Statement::new(StatementType::Insert { row })), + Err(e) => Err(e), } } - _ => { - Err(String::from("could not parse insert statement")) - } + _ => Err(String::from("could not parse insert statement")), } } else if command.starts_with("select") { Ok(Statement::new(StatementType::Select)) } else { - return Err(String::from("Could not parse command")); + Err(String::from("Could not parse command")) } } - pub(crate) fn execute(&self) { + pub(crate) fn execute(&self, table: &mut Table) -> ExecuteResult { match &self.statement_type { - StatementType::Insert { row } => { - println!("This is where you do an insert {}", row.id) - } + StatementType::Insert { row } => match row.serialize() { + Err(e) => ExecuteResult::Error { + error: e.to_string(), + }, + Ok(serialization) => { + table.append_row(serialization); + ExecuteResult::Success + } + }, StatementType::Select => { - println!("This is where you do an select") + let rows = table.get_rows(); + for row in rows { + row.print() + } + + return ExecuteResult::Success; } } } } - - diff --git a/src/table.rs b/src/table.rs new file mode 100644 index 0000000..51ef4e9 --- /dev/null +++ b/src/table.rs @@ -0,0 +1,61 @@ +use crate::pager::Pager; +use crate::row; +use std::mem::size_of; +use std::ops::DerefMut; +use crate::row::Row; +use crate::row::Serialize; + +const PAGE_SIZE: usize = 4096; + +pub struct Table { + num_rows: usize, + pager: Pager, +} + +impl Table { + pub fn new() -> Self { + Self { + num_rows: 0, + pager: Pager::new(PAGE_SIZE), + } + } + + pub(crate) fn append_row(&mut self, row: Vec) { + let row_size = size_of::(); + let page_num = self.num_rows / self.rows_per_page(row_size); + let row_offset = self.num_rows % self.rows_per_page(row_size); + + let page = self.pager.get_mut_or_alloc(page_num); + + let byte_offset = row_offset * row_size; + + page.deref_mut() + .splice(byte_offset..byte_offset + row_size, row); + self.num_rows += 1; + } + + pub fn row(&mut self, row_num: usize) -> Row { + let row_size = size_of::(); + let page_num = row_num / self.rows_per_page(row_size); + let row_offset = row_num % self.rows_per_page(row_size); + let byte_offset = row_offset * row_size; + + let page = self.pager.get_mut_or_alloc(page_num); + + Row::deserialize(&page[byte_offset..byte_offset + row_size]).unwrap() + } + + #[inline] + fn rows_per_page(&self, row_size: usize) -> usize { + PAGE_SIZE / row_size + } + + pub(crate) fn get_rows(&mut self) -> Vec { + let mut rows: Vec = Vec::with_capacity(self.num_rows); + for i in 0..self.num_rows { + rows.push(self.row(i)) + } + + rows + } +}