Add basic memory
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Kasper Juul Hermansen 2022-03-29 22:11:49 +02:00
parent 5ae8b48845
commit 63f4dce705
8 changed files with 311 additions and 71 deletions

46
Cargo.lock generated
View File

@ -11,6 +11,24 @@ dependencies = [
"memchr", "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]] [[package]]
name = "const_format" name = "const_format"
version = "0.2.22" version = "0.2.22"
@ -78,10 +96,32 @@ version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" 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]] [[package]]
name = "sqlite_clone" name = "sqlite_clone"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"bincode",
"serde",
"sscanf", "sscanf",
] ]
@ -125,3 +165,9 @@ name = "unicode-xid"
version = "0.2.2" version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "virtue"
version = "0.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "757cfbfe0d17ee6f22fe97e536d463047d451b47cf9d11e2b7d1398b0ef274dd"

View File

@ -7,3 +7,5 @@ edition = "2021"
[dependencies] [dependencies]
sscanf = {version="0.2.1"} sscanf = {version="0.2.1"}
bincode = "2.0.0-rc.1"
serde = {version="1.0.136", features=["derive"]}

View File

@ -1,18 +1,25 @@
mod page;
mod pager;
mod row;
mod statement; mod statement;
mod table;
use crate::statement::ExecuteResult;
use crate::table::Table;
use std::io; use std::io;
use std::io::Write; use std::io::Write;
use std::process::exit; use std::process::exit;
struct InputBuffer { struct InputBuffer {
buffer: Option<String>, buffer: Option<String>,
table: Table,
} }
impl InputBuffer { impl InputBuffer {
pub fn new() -> Self { pub fn new() -> Self {
return 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 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 { match &self.buffer {
Some(command) => { Some(command) => {
if command.starts_with(".") { if command.starts_with(".") {
@ -46,7 +53,15 @@ impl InputBuffer {
} else { } else {
match Self::prepare_statement(&command.replace("\n", "")) { match Self::prepare_statement(&command.replace("\n", "")) {
Ok(statement) => { Ok(statement) => {
statement.execute() let execution_result = statement.execute(&mut self.table);
match execution_result {
ExecuteResult::Success => {
println!("success")
}
_ => {
println!("failure")
}
}
} }
Err(e) => { Err(e) => {
println!("{}", e); println!("{}", e);
@ -74,14 +89,10 @@ impl InputBuffer {
} }
} }
fn main() -> Result<(), String> { fn main() -> Result<(), String> {
let mut input_buffer = InputBuffer::new(); let mut input_buffer = InputBuffer::new();
loop { loop {
input_buffer input_buffer.print_prompt().read_input()?.parse()
.print_prompt()
.read_input()?
.parse()
} }
} }

28
src/page.rs Normal file
View File

@ -0,0 +1,28 @@
use std::ops::{Deref, DerefMut};
#[derive(Debug, Clone)]
pub struct Page {
buffer: Vec<u8>,
}
impl Page {
pub fn new(buffer: Vec<u8>) -> Self {
Self {
buffer,
}
}
}
impl Deref for Page {
type Target = Vec<u8>;
fn deref(&self) -> &Self::Target {
&self.buffer
}
}
impl DerefMut for Page {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.buffer
}
}

35
src/pager.rs Normal file
View File

@ -0,0 +1,35 @@
use crate::page::Page;
pub struct Pager {
pages: Vec<Option<Page>>,
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)
}
}

84
src/row.rs Normal file
View File

@ -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<Vec<u8>, EncodeError>;
fn deserialize(buffer: &[u8]) -> Result<Self, DecodeError>
where
Self: Sized;
}
impl Serialize for Row {
fn serialize(&self) -> Result<Vec<u8>, EncodeError> {
bincode::encode_to_vec(self, bincode::config::standard())
}
fn deserialize(buffer: &[u8]) -> Result<Self, DecodeError> {
match bincode::decode_from_slice::<Self, Configuration>(buffer, bincode::config::standard())
{
Ok((row, _)) => Ok(row),
Err(e) => Err(e),
}
}
}
impl Row {
pub fn new(id: u32, username: String, email: String) -> Result<Self, String> {
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<usize> {
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::Item> {
self.inner.next()
}
}

View File

@ -1,50 +1,16 @@
use std::io::Write; use crate::row::Serialize;
use std::mem::size_of; use crate::table::Table;
use crate::{row};
pub enum StatementType { pub enum StatementType {
Insert { Insert { row: row::Row },
row: Row
},
Select, Select,
} }
const COLUMN_USERNAME_SIZE: usize = 32; pub enum ExecuteResult {
const COLUMN_EMAIL_SIZE: usize = 255; Error { error: String },
Success,
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<Self, String> {
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<usize> {
bytes.write(source)
} }
pub struct Statement { pub struct Statement {
@ -53,43 +19,50 @@ pub struct Statement {
impl Statement { impl Statement {
pub fn new(statement_type: StatementType) -> Self { pub fn new(statement_type: StatementType) -> Self {
Self { Self { statement_type }
statement_type,
}
} }
pub fn parse_statement(command: &String) -> Result<Self, String> { pub fn parse_statement(command: &String) -> Result<Self, String> {
if command.starts_with("insert") { if command.starts_with("insert") {
match sscanf::scanf!(command, "insert {} {} {}", usize, str, str) { match sscanf::scanf!(command, "insert {} {} {}", usize, str, str) {
Ok((id, username, email)) => { Ok((id, username, email)) => {
match Row::new(u32::try_from(id).unwrap(), username.to_string(), email.to_string()) { match row::Row::new(
Ok(row) => { u32::try_from(id).unwrap(),
Ok(Statement::new(StatementType::Insert { row: row })) username.to_string(),
} email.to_string(),
Err(e) => { Err(e) } ) {
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") { } else if command.starts_with("select") {
Ok(Statement::new(StatementType::Select)) Ok(Statement::new(StatementType::Select))
} else { } 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 { match &self.statement_type {
StatementType::Insert { row } => { StatementType::Insert { row } => match row.serialize() {
println!("This is where you do an insert {}", row.id) Err(e) => ExecuteResult::Error {
error: e.to_string(),
},
Ok(serialization) => {
table.append_row(serialization);
ExecuteResult::Success
} }
},
StatementType::Select => { StatementType::Select => {
println!("This is where you do an select") let rows = table.get_rows();
} for row in rows {
} row.print()
}
} }
return ExecuteResult::Success;
}
}
}
}

61
src/table.rs Normal file
View File

@ -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<u8>) {
let row_size = size_of::<row::Row>();
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::<row::Row>();
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<Row> {
let mut rows: Vec<Row> = Vec::with_capacity(self.num_rows);
for i in 0..self.num_rows {
rows.push(self.row(i))
}
rows
}
}