all clippy messages fixed

This commit is contained in:
Sander Hautvast 2022-10-30 15:06:50 +01:00
parent ccf800340a
commit 75020726dc
7 changed files with 136 additions and 141 deletions

View file

@ -1,13 +1,13 @@
use std::mem; use crate::database::SchemaRecord;
use crate::database::{Database, SchemaRecord};
use crate::page::{self, Page}; use crate::page::{self, Page};
use crate::record::Record; use crate::record::Record;
use std::mem;
pub struct Builder { pub struct DatabaseBuilder {
current_page: Page, pub current_page: Page,
n_records_on_current_page: u16, pub n_records_on_current_page: u16,
leaf_pages: Vec<Page>, pub leaf_pages: Vec<Page>,
schema: Option<SchemaRecord>, pub schema: Option<SchemaRecord>,
} }
fn new_page() -> Page { fn new_page() -> Page {
@ -16,7 +16,7 @@ fn new_page() -> Page {
page page
} }
impl Builder { impl DatabaseBuilder {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
current_page: new_page(), current_page: new_page(),
@ -29,14 +29,16 @@ impl Builder {
pub fn add_record(&mut self, record: Record) { pub fn add_record(&mut self, record: Record) {
if self.current_page_is_full(&record) { if self.current_page_is_full(&record) {
self.finish_current_page(); self.finish_current_page();
self.leaf_pages.push(mem::replace(&mut self.current_page, new_page())); self.leaf_pages
.push(mem::replace(&mut self.current_page, new_page()));
self.n_records_on_current_page = 0; self.n_records_on_current_page = 0;
} }
self.current_page.key = record.rowid; //clone? self.current_page.key = record.rowid; //clone?
let bytes: Vec<u8> = record.into(); let bytes: Vec<u8> = record.into();
self.current_page.put_bytes_bw(&bytes); self.current_page.put_bytes_bw(&bytes);
self.current_page.put_u16(self.current_page.bw_position as u16); self.current_page
.put_u16(self.current_page.bw_position as u16);
self.n_records_on_current_page += 1; self.n_records_on_current_page += 1;
} }
@ -54,46 +56,3 @@ impl Builder {
self.current_page.put_u16(self.current_page.bw_position); self.current_page.put_u16(self.current_page.bw_position);
} }
} }
impl Into<Database> for Builder {
fn into(mut self) -> Database {
self.current_page.fw_position = page::POSITION_CELL_COUNT;
self.current_page.put_u16(self.n_records_on_current_page);
if self.n_records_on_current_page > 0 {
self.current_page.put_u16(self.current_page.bw_position);
} else {
self.current_page.put_u16(self.current_page.bw_position - 1);
}
self.leaf_pages.push(self.current_page);
Database::new(self.schema.unwrap(), self.leaf_pages) //panics is schema is not set
}
}
#[cfg(test)]
mod tests {
use std::fs::File;
use std::io::{BufWriter, Error};
use crate::database::write;
use crate::values;
use super::*;
#[test]
fn test_build() -> Result<(), Error> {
let mut builder = Builder::new();
builder.schema(
"foo",
"create table foo(bar varchar(10))",
);
let mut record = Record::new(1);
record.add_value(values::string("helloworld"));
builder.add_record(record);
let database: Database = builder.into();
let file = File::create("foo.db")?;
let writer = BufWriter::new(file);
write(database, writer)?;
Ok(())
}
}

View file

@ -1,10 +1,10 @@
use std::io::{BufWriter, Error, Write}; use crate::builder::DatabaseBuilder;
use std::mem;
use crate::varint;
use crate::page; use crate::page;
use crate::page::{Page, PageType}; use crate::page::{Page, PageType};
use crate::record::Record; use crate::record::Record;
use crate::values; use crate::varint;
use std::io::{BufWriter, Error, Write};
use std::mem;
pub struct Database { pub struct Database {
schema: SchemaRecord, schema: SchemaRecord,
@ -13,18 +13,31 @@ pub struct Database {
impl Database { impl Database {
pub fn new(schema: SchemaRecord, leaf_pages: Vec<Page>) -> Self { pub fn new(schema: SchemaRecord, leaf_pages: Vec<Page>) -> Self {
Self { Self { schema, leaf_pages }
schema,
leaf_pages,
} }
}
impl From<DatabaseBuilder> for Database {
fn from(mut dbb: DatabaseBuilder) -> Self {
dbb.current_page.fw_position = page::POSITION_CELL_COUNT;
dbb.current_page.put_u16(dbb.n_records_on_current_page);
if dbb.n_records_on_current_page > 0 {
dbb.current_page.put_u16(dbb.current_page.bw_position);
} else {
dbb.current_page.put_u16(dbb.current_page.bw_position - 1);
}
dbb.leaf_pages.push(dbb.current_page);
Database::new(dbb.schema.unwrap(), dbb.leaf_pages) //panics is schema is not set
} }
} }
pub struct SchemaRecord { pub struct SchemaRecord {
rowid: u64, pub rowid: u64,
table_name: String, pub table_name: String,
root_page: u32, pub root_page: u32,
sql: String, pub sql: String,
} }
impl SchemaRecord { impl SchemaRecord {
@ -38,22 +51,11 @@ impl SchemaRecord {
} }
} }
impl Into<Record> for SchemaRecord {
fn into(self) -> Record {
let mut record = Record::new(self.rowid);
record.add_value(values::string("table"));
record.add_value(values::string(&self.table_name.to_ascii_lowercase()));
record.add_value(values::string(&self.table_name.to_ascii_lowercase()));
record.add_value(values::integer(self.root_page as i64));
record.add_value(values::string(&self.sql));
record
}
}
pub fn write<W: Write>(database: Database, mut writer: BufWriter<W>) -> Result<(), Error> { pub fn write<W: Write>(database: Database, mut writer: BufWriter<W>) -> Result<(), Error> {
let mut current_top_layer = database.leaf_pages; let mut current_top_layer = database.leaf_pages;
let mut n_pages = current_top_layer.len(); let mut n_pages = current_top_layer.len();
while current_top_layer.len() > 1 { // db needs interior pages? while current_top_layer.len() > 1 {
// db needs interior pages?
current_top_layer = create_interior_pages(current_top_layer); current_top_layer = create_interior_pages(current_top_layer);
n_pages += current_top_layer.len(); n_pages += current_top_layer.len();
} }
@ -103,7 +105,7 @@ fn set_page_references(page: &mut Page) {
} }
fn write_pages<W: Write>(writer: &mut BufWriter<W>, page: &Page) -> Result<(), Error> { fn write_pages<W: Write>(writer: &mut BufWriter<W>, page: &Page) -> Result<(), Error> {
writer.write(&page.data)?; writer.write_all(&page.data)?;
for child in page.children.iter() { for child in page.children.iter() {
write_pages(writer, child)?; write_pages(writer, child)?;
} }
@ -121,7 +123,7 @@ fn create_header_page(n_pages: usize, schema: SchemaRecord) -> Page {
header_page.put_u16(payload_location); //payload start header_page.put_u16(payload_location); //payload start
header_page.put_u8(0); // the number of fragmented free bytes within the cell content area header_page.put_u8(0); // the number of fragmented free bytes within the cell content area
header_page.put_u16(payload_location); // first cell header_page.put_u16(payload_location); // first cell
return header_page; header_page
} }
fn write_schema(root_page: &mut Page, schema_record: SchemaRecord) -> u16 { fn write_schema(root_page: &mut Page, schema_record: SchemaRecord) -> u16 {
@ -131,18 +133,17 @@ fn write_schema(root_page: &mut Page, schema_record: SchemaRecord) -> u16 {
root_page.bw_position root_page.bw_position
} }
fn create_interior_pages(mut child_pages: Vec<Page>) -> Vec<Page> { fn create_interior_pages(child_pages: Vec<Page>) -> Vec<Page> {
let mut interior_pages = Vec::new(); let mut interior_pages = Vec::new();
let mut interior_page = Page::new_interior(); let mut interior_page = Page::new_interior();
interior_page.key = child_pages.iter().map(|p| p.key).max().unwrap(); interior_page.key = child_pages.iter().map(|p| p.key).max().unwrap();
interior_page.fw_position = page::START_OF_INTERIOR_PAGE; interior_page.fw_position = page::START_OF_INTERIOR_PAGE;
let mut page_index = 0;
let children_length = child_pages.len(); let children_length = child_pages.len();
let mut child_count = 0;
let mut last_leaf: Page = Page::new_leaf(); // have to assign :( let mut last_leaf: Page = Page::new_leaf(); // have to assign :(
for mut leaf_page in child_pages { for (child_count, mut leaf_page) in child_pages.into_iter().enumerate() {
if child_count < children_length - 1 { if child_count < children_length - 1 {
if interior_page.bw_position <= interior_page.fw_position + 15 { // 15 is somewhat arbitrary if interior_page.bw_position <= interior_page.fw_position + 15 {
// 15 is somewhat arbitrary
interior_page.fw_position = page::START_OF_CONTENT_AREA; interior_page.fw_position = page::START_OF_CONTENT_AREA;
interior_page.put_u16(interior_page.bw_position); interior_page.put_u16(interior_page.bw_position);
interior_page.put_bytes(&[0, 0, 0, 0, 0]); interior_page.put_bytes(&[0, 0, 0, 0, 0]);
@ -152,11 +153,9 @@ fn create_interior_pages(mut child_pages: Vec<Page>) -> Vec<Page> {
} }
create_cell(&mut leaf_page); create_cell(&mut leaf_page);
interior_page.add_child(leaf_page); interior_page.add_child(leaf_page);
page_index += 1;
} else { } else {
last_leaf = leaf_page; last_leaf = leaf_page;
} }
child_count += 1;
} }
interior_page.fw_position = page::START_OF_CONTENT_AREA; interior_page.fw_position = page::START_OF_CONTENT_AREA;
@ -184,8 +183,8 @@ fn write_header(rootpage: &mut Page, n_pages: u32) {
rootpage.put_u8(MIN_EMBED_PAYLOAD_FRACTION); rootpage.put_u8(MIN_EMBED_PAYLOAD_FRACTION);
rootpage.put_u8(LEAF_PAYLOAD_FRACTION); rootpage.put_u8(LEAF_PAYLOAD_FRACTION);
rootpage.put_u32(FILECHANGE_COUNTER); rootpage.put_u32(FILECHANGE_COUNTER);
rootpage.put_u32(n_pages);// file size in pages rootpage.put_u32(n_pages); // file size in pages
rootpage.put_u32(FREELIST_TRUNK_PAGE_HUMBER);// Page number of the first freelist trunk page. rootpage.put_u32(FREELIST_TRUNK_PAGE_HUMBER); // Page number of the first freelist trunk page.
rootpage.put_u32(TOTAL_N_FREELIST_PAGES); rootpage.put_u32(TOTAL_N_FREELIST_PAGES);
rootpage.put_u32(SCHEMA_COOKIE); rootpage.put_u32(SCHEMA_COOKIE);
rootpage.put_u32(SQLITE_SCHEMAVERSION); rootpage.put_u32(SQLITE_SCHEMAVERSION);
@ -193,17 +192,19 @@ fn write_header(rootpage: &mut Page, n_pages: u32) {
rootpage.put_u32(LARGEST_ROOT_BTREE_PAGE); rootpage.put_u32(LARGEST_ROOT_BTREE_PAGE);
rootpage.put_u32(ENCODING_UTF8); rootpage.put_u32(ENCODING_UTF8);
rootpage.put_u32(USER_VERSION); rootpage.put_u32(USER_VERSION);
rootpage.put_u32(VACUUM_MODE_OFF);// True (non-zero) for incremental-vacuum mode. False (zero) otherwise. rootpage.put_u32(VACUUM_MODE_OFF); // True (non-zero) for incremental-vacuum mode. False (zero) otherwise.
rootpage.put_u32(APP_ID);// Application ID rootpage.put_u32(APP_ID); // Application ID
rootpage.put_bytes(&FILLER);// Reserved for expansion. Must be zero. rootpage.put_bytes(&FILLER); // Reserved for expansion. Must be zero.
rootpage.put_bytes(&VERSION_VALID_FOR);// The version-valid-for number rootpage.put_bytes(&VERSION_VALID_FOR); // The version-valid-for number
rootpage.put_bytes(&SQLITE_VERSION);// SQLITE_VERSION_NUMBER rootpage.put_bytes(&SQLITE_VERSION); // SQLITE_VERSION_NUMBER
rootpage.put_u8(TABLE_LEAF_PAGE); // leaf table b-tree page for schema rootpage.put_u8(TABLE_LEAF_PAGE); // leaf table b-tree page for schema
rootpage.put_u16(NO_FREE_BLOCKS); // zero if there are no freeblocks rootpage.put_u16(NO_FREE_BLOCKS); // zero if there are no freeblocks
rootpage.put_u16(1); // the number of cells on this page rootpage.put_u16(1); // the number of cells on this page
} }
const MAGIC_HEADER: [u8; 16] = [0x53, 0x51, 0x4c, 0x69, 0x74, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x20, 0x33, 0x00]; const MAGIC_HEADER: [u8; 16] = [
0x53, 0x51, 0x4c, 0x69, 0x74, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x20, 0x33, 0x00,
];
pub const DEFAULT_PAGE_SIZE: u16 = 4096; pub const DEFAULT_PAGE_SIZE: u16 = 4096;
const FILE_FORMAT_WRITE_VERSION: u8 = 1; const FILE_FORMAT_WRITE_VERSION: u8 = 1;
const FILE_FORMAT_READ_VERSION: u8 = 1; const FILE_FORMAT_READ_VERSION: u8 = 1;
@ -222,7 +223,10 @@ const ENCODING_UTF8: u32 = 1;
const USER_VERSION: u32 = 0; const USER_VERSION: u32 = 0;
const VACUUM_MODE_OFF: u32 = 0; const VACUUM_MODE_OFF: u32 = 0;
const APP_ID: u32 = 0; const APP_ID: u32 = 0;
const FILLER: [u8; 20] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; const FILLER: [u8; 20] = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
];
const VERSION_VALID_FOR: [u8; 4] = [0, 0, 0x03, 250]; const VERSION_VALID_FOR: [u8; 4] = [0, 0, 0x03, 250];
const SQLITE_VERSION: [u8; 4] = [0x00, 0x2e, 0x5F, 0x1A]; const SQLITE_VERSION: [u8; 4] = [0x00, 0x2e, 0x5F, 0x1A];
const NO_FREE_BLOCKS: u16 = 0; const NO_FREE_BLOCKS: u16 = 0;
@ -230,5 +234,3 @@ pub const TABLE_LEAF_PAGE: u8 = 0x0d;
pub const TABLE_INTERIOR_PAGE: u8 = 0x05; pub const TABLE_INTERIOR_PAGE: u8 = 0x05;
const INDEX_LEAF_PAGE: u8 = 0x0a; const INDEX_LEAF_PAGE: u8 = 0x0a;
const INDEX_INTERIOR_PAGE: u8 = 0x02; const INDEX_INTERIOR_PAGE: u8 = 0x02;

View file

@ -1,18 +1,33 @@
#![allow(dead_code)] #![allow(dead_code)]
mod page; mod builder;
mod database; mod database;
mod page;
mod record;
mod values; mod values;
mod varint; mod varint;
mod record;
mod builder;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::builder::DatabaseBuilder;
use crate::database::{write, Database};
use crate::record::Record;
use crate::values;
use std::fs::File;
use std::io::{BufWriter, Error};
#[test] #[test]
fn it_works() { fn test_build() -> Result<(), Error> {
let result = 2 + 2; let mut builder = DatabaseBuilder::new();
assert_eq!(result, 4); builder.schema("foo", "create table foo(bar varchar(10))");
let mut record = Record::new(1);
record.add_value(values::string("helloworld"));
builder.add_record(record);
let database: Database = builder.into();
let file = File::create("foo.db")?;
let writer = BufWriter::new(file);
write(database, writer)?;
Ok(())
} }
} }

View file

@ -1,5 +1,5 @@
use byteorder::{BigEndian, ByteOrder};
use crate::database; use crate::database;
use byteorder::{BigEndian, ByteOrder};
pub const POSITION_CELL_COUNT: u16 = 3; pub const POSITION_CELL_COUNT: u16 = 3;
pub const START_OF_CONTENT_AREA: u16 = 5; pub const START_OF_CONTENT_AREA: u16 = 5;
@ -116,7 +116,7 @@ impl Page {
pub fn get_u16(&self) -> u16 { pub fn get_u16(&self) -> u16 {
let position = self.fw_position as usize; let position = self.fw_position as usize;
(self.data[position] as u16) << 8 + self.data[position + 1] ((self.data[position] as u16) << 8) + (self.data[position + 1]) as u16
// does not increase the fw pointerr // does not increase the fw pointerr
} }
} }

View file

@ -1,3 +1,4 @@
use crate::database::SchemaRecord;
use crate::values::*; use crate::values::*;
use crate::varint; use crate::varint;
@ -21,42 +22,54 @@ impl Record {
/// length of the byte representation /// length of the byte representation
pub fn bytes_len(&self) -> u16 { pub fn bytes_len(&self) -> u16 {
let record_length: u16 = self.values.iter() let record_length: u16 = self.values.iter().map(|v| v.len()).sum();
.map(|v| v.len()) record_length + 1
.sum();
record_length+1
} }
} }
impl Into<Vec<u8>> for Record{ impl From<Record> for Vec<u8> {
fn into(mut self) -> Vec<u8> { fn from(mut record: Record) -> Vec<u8> {
let record_length = self.bytes_len(); let record_length = record.bytes_len();
let mut length_bytes = varint::write(record_length as u64); let mut length_bytes = varint::write(record_length as u64);
let mut rowid_bytes = varint::write(self.rowid); let mut rowid_bytes = varint::write(record.rowid);
let mut buffer = Vec::with_capacity(length_bytes.len() + rowid_bytes.len() + record_length as usize); let mut buffer =
Vec::with_capacity(length_bytes.len() + rowid_bytes.len() + record_length as usize);
buffer.append(&mut length_bytes); buffer.append(&mut length_bytes);
buffer.append(&mut rowid_bytes); buffer.append(&mut rowid_bytes);
// 'The initial portion of the payload that does not spill to overflow pages.' // 'The initial portion of the payload that does not spill to overflow pages.'
let length_of_encoded_column_types: usize = self.values.iter() let length_of_encoded_column_types: usize =
.map(|v| v.datatype.len()) record.values.iter().map(|v| v.datatype.len()).sum();
.sum(); buffer.append(&mut varint::write(
buffer.append(&mut varint::write((length_of_encoded_column_types + 1) as u64)); (length_of_encoded_column_types + 1) as u64,
));
//write all types //write all types
for v in self.values.iter_mut() { for v in record.values.iter_mut() {
buffer.append(&mut v.datatype) buffer.append(&mut v.datatype)
} }
// write all values // write all values
for v in self.values.iter_mut() { for v in record.values.iter_mut() {
buffer.append(&mut v.data) buffer.append(&mut v.data)
} }
buffer buffer
} }
} }
impl From<SchemaRecord> for Record {
fn from(s: SchemaRecord) -> Self {
let mut record = Record::new(s.rowid);
record.add_value(string("table"));
record.add_value(string(&s.table_name.to_ascii_lowercase()));
record.add_value(string(&s.table_name.to_ascii_lowercase()));
record.add_value(integer(s.root_page as i64));
record.add_value(string(&s.sql));
record
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View file

@ -1,12 +1,11 @@
use byteorder::{BigEndian, ByteOrder};
use crate::varint; use crate::varint;
use byteorder::{BigEndian, ByteOrder};
pub struct Value { pub struct Value {
pub datatype: Vec<u8>, pub datatype: Vec<u8>,
pub data: Vec<u8>, pub data: Vec<u8>,
} }
impl Value { impl Value {
pub fn len(&self) -> u16 { pub fn len(&self) -> u16 {
(self.datatype.len() + self.data.len()) as u16 (self.datatype.len() + self.data.len()) as u16
@ -15,21 +14,33 @@ impl Value {
pub fn string(value: &str) -> Value { pub fn string(value: &str) -> Value {
let bytes = value.chars().map(|c| c as u8).collect::<Vec<_>>(); let bytes = value.chars().map(|c| c as u8).collect::<Vec<_>>();
Value { datatype: varint::write((bytes.len() * 2 + 13) as u64), data: bytes } Value {
datatype: varint::write((bytes.len() * 2 + 13) as u64),
data: bytes,
}
} }
pub fn blob(value: Vec<u8>) -> Value { pub fn blob(value: Vec<u8>) -> Value {
Value { datatype: varint::write((value.len() * 2 + 12) as u64), data: value } Value {
datatype: varint::write((value.len() * 2 + 12) as u64),
data: value,
}
} }
pub fn integer(value: i64) -> Value { pub fn integer(value: i64) -> Value {
Value { datatype: get_int_type(value), data: sqlite_integer_to_bytes(value) } Value {
datatype: get_int_type(value),
data: sqlite_integer_to_bytes(value),
}
} }
pub fn float(value: f64) -> Value { pub fn float(value: f64) -> Value {
let mut buffer = [0_u8; 8]; let mut buffer = [0_u8; 8];
BigEndian::write_f64(&mut buffer, value); BigEndian::write_f64(&mut buffer, value);
Value { datatype: vec![7], data: buffer.to_vec() } Value {
datatype: vec![7],
data: buffer.to_vec(),
}
} }
pub fn len(value: &Value) -> usize { pub fn len(value: &Value) -> usize {
@ -73,12 +84,7 @@ fn get_int_type(value: i64) -> Vec<u8> {
} }
fn get_length_of_byte_encoding(value: i64) -> u8 { fn get_length_of_byte_encoding(value: i64) -> u8 {
let u = let u = if value < 0 { !value } else { value };
if value < 0 {
!value
} else {
value
};
if u <= 127 { if u <= 127 {
1 1
} else if u <= 32767 { } else if u <= 32767 {
@ -96,7 +102,6 @@ fn get_length_of_byte_encoding(value: i64) -> u8 {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::values::Value;
use super::*; use super::*;
#[test] #[test]

View file

@ -29,13 +29,14 @@ mod tests {
#[test] #[test]
fn test() { fn test() {
assert_eq!(vec![0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF], write(0xffffffffffffffff)); assert_eq!(
vec![0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
write(0xffffffffffffffff)
);
} }
#[test] #[test]
fn test_write1() { fn test_write1() {
let a:i16 = -1;
println!("{}", a as u16);
assert_eq!(vec![1], write(0x01)); assert_eq!(vec![1], write(0x01));
} }
} }