all clippy messages fixed
This commit is contained in:
parent
ccf800340a
commit
75020726dc
7 changed files with 136 additions and 141 deletions
|
|
@ -1,13 +1,13 @@
|
|||
use std::mem;
|
||||
use crate::database::{Database, SchemaRecord};
|
||||
use crate::database::SchemaRecord;
|
||||
use crate::page::{self, Page};
|
||||
use crate::record::Record;
|
||||
use std::mem;
|
||||
|
||||
pub struct Builder {
|
||||
current_page: Page,
|
||||
n_records_on_current_page: u16,
|
||||
leaf_pages: Vec<Page>,
|
||||
schema: Option<SchemaRecord>,
|
||||
pub struct DatabaseBuilder {
|
||||
pub current_page: Page,
|
||||
pub n_records_on_current_page: u16,
|
||||
pub leaf_pages: Vec<Page>,
|
||||
pub schema: Option<SchemaRecord>,
|
||||
}
|
||||
|
||||
fn new_page() -> Page {
|
||||
|
|
@ -16,7 +16,7 @@ fn new_page() -> Page {
|
|||
page
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
impl DatabaseBuilder {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
current_page: new_page(),
|
||||
|
|
@ -29,14 +29,16 @@ impl Builder {
|
|||
pub fn add_record(&mut self, record: Record) {
|
||||
if self.current_page_is_full(&record) {
|
||||
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.current_page.key = record.rowid; //clone?
|
||||
let bytes: Vec<u8> = record.into();
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -54,46 +56,3 @@ impl Builder {
|
|||
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(())
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
use std::io::{BufWriter, Error, Write};
|
||||
use std::mem;
|
||||
use crate::varint;
|
||||
use crate::builder::DatabaseBuilder;
|
||||
use crate::page;
|
||||
use crate::page::{Page, PageType};
|
||||
use crate::record::Record;
|
||||
use crate::values;
|
||||
use crate::varint;
|
||||
use std::io::{BufWriter, Error, Write};
|
||||
use std::mem;
|
||||
|
||||
pub struct Database {
|
||||
schema: SchemaRecord,
|
||||
|
|
@ -13,18 +13,31 @@ pub struct Database {
|
|||
|
||||
impl Database {
|
||||
pub fn new(schema: SchemaRecord, leaf_pages: Vec<Page>) -> Self {
|
||||
Self {
|
||||
schema,
|
||||
leaf_pages,
|
||||
Self { 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 {
|
||||
rowid: u64,
|
||||
table_name: String,
|
||||
root_page: u32,
|
||||
sql: String,
|
||||
pub rowid: u64,
|
||||
pub table_name: String,
|
||||
pub root_page: u32,
|
||||
pub sql: String,
|
||||
}
|
||||
|
||||
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> {
|
||||
let mut current_top_layer = database.leaf_pages;
|
||||
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);
|
||||
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> {
|
||||
writer.write(&page.data)?;
|
||||
writer.write_all(&page.data)?;
|
||||
for child in page.children.iter() {
|
||||
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_u8(0); // the number of fragmented free bytes within the cell content area
|
||||
header_page.put_u16(payload_location); // first cell
|
||||
return header_page;
|
||||
header_page
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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_page = Page::new_interior();
|
||||
interior_page.key = child_pages.iter().map(|p| p.key).max().unwrap();
|
||||
interior_page.fw_position = page::START_OF_INTERIOR_PAGE;
|
||||
let mut page_index = 0;
|
||||
let children_length = child_pages.len();
|
||||
let mut child_count = 0;
|
||||
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 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.put_u16(interior_page.bw_position);
|
||||
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);
|
||||
interior_page.add_child(leaf_page);
|
||||
page_index += 1;
|
||||
} else {
|
||||
last_leaf = leaf_page;
|
||||
}
|
||||
child_count += 1;
|
||||
}
|
||||
|
||||
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(LEAF_PAYLOAD_FRACTION);
|
||||
rootpage.put_u32(FILECHANGE_COUNTER);
|
||||
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(n_pages); // file size in pages
|
||||
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(SCHEMA_COOKIE);
|
||||
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(ENCODING_UTF8);
|
||||
rootpage.put_u32(USER_VERSION);
|
||||
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_bytes(&FILLER);// Reserved for expansion. Must be zero.
|
||||
rootpage.put_bytes(&VERSION_VALID_FOR);// The version-valid-for number
|
||||
rootpage.put_bytes(&SQLITE_VERSION);// SQLITE_VERSION_NUMBER
|
||||
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_bytes(&FILLER); // Reserved for expansion. Must be zero.
|
||||
rootpage.put_bytes(&VERSION_VALID_FOR); // The version-valid-for number
|
||||
rootpage.put_bytes(&SQLITE_VERSION); // SQLITE_VERSION_NUMBER
|
||||
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(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;
|
||||
const FILE_FORMAT_WRITE_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 VACUUM_MODE_OFF: 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 SQLITE_VERSION: [u8; 4] = [0x00, 0x2e, 0x5F, 0x1A];
|
||||
const NO_FREE_BLOCKS: u16 = 0;
|
||||
|
|
@ -230,5 +234,3 @@ pub const TABLE_LEAF_PAGE: u8 = 0x0d;
|
|||
pub const TABLE_INTERIOR_PAGE: u8 = 0x05;
|
||||
const INDEX_LEAF_PAGE: u8 = 0x0a;
|
||||
const INDEX_INTERIOR_PAGE: u8 = 0x02;
|
||||
|
||||
|
||||
|
|
|
|||
29
src/lib.rs
29
src/lib.rs
|
|
@ -1,18 +1,33 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
mod page;
|
||||
mod builder;
|
||||
mod database;
|
||||
mod page;
|
||||
mod record;
|
||||
mod values;
|
||||
mod varint;
|
||||
mod record;
|
||||
mod builder;
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
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]
|
||||
fn it_works() {
|
||||
let result = 2 + 2;
|
||||
assert_eq!(result, 4);
|
||||
fn test_build() -> Result<(), Error> {
|
||||
let mut builder = DatabaseBuilder::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(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use byteorder::{BigEndian, ByteOrder};
|
||||
use crate::database;
|
||||
use byteorder::{BigEndian, ByteOrder};
|
||||
|
||||
pub const POSITION_CELL_COUNT: u16 = 3;
|
||||
pub const START_OF_CONTENT_AREA: u16 = 5;
|
||||
|
|
@ -116,7 +116,7 @@ impl Page {
|
|||
|
||||
pub fn get_u16(&self) -> u16 {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use crate::database::SchemaRecord;
|
||||
use crate::values::*;
|
||||
use crate::varint;
|
||||
|
||||
|
|
@ -21,42 +22,54 @@ impl Record {
|
|||
|
||||
/// length of the byte representation
|
||||
pub fn bytes_len(&self) -> u16 {
|
||||
let record_length: u16 = self.values.iter()
|
||||
.map(|v| v.len())
|
||||
.sum();
|
||||
record_length+1
|
||||
let record_length: u16 = self.values.iter().map(|v| v.len()).sum();
|
||||
record_length + 1
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Vec<u8>> for Record{
|
||||
fn into(mut self) -> Vec<u8> {
|
||||
let record_length = self.bytes_len();
|
||||
impl From<Record> for Vec<u8> {
|
||||
fn from(mut record: Record) -> Vec<u8> {
|
||||
let record_length = record.bytes_len();
|
||||
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 rowid_bytes);
|
||||
|
||||
// 'The initial portion of the payload that does not spill to overflow pages.'
|
||||
let length_of_encoded_column_types: usize = self.values.iter()
|
||||
.map(|v| v.datatype.len())
|
||||
.sum();
|
||||
buffer.append(&mut varint::write((length_of_encoded_column_types + 1) as u64));
|
||||
let length_of_encoded_column_types: usize =
|
||||
record.values.iter().map(|v| v.datatype.len()).sum();
|
||||
buffer.append(&mut varint::write(
|
||||
(length_of_encoded_column_types + 1) as u64,
|
||||
));
|
||||
|
||||
//write all types
|
||||
for v in self.values.iter_mut() {
|
||||
for v in record.values.iter_mut() {
|
||||
buffer.append(&mut v.datatype)
|
||||
}
|
||||
|
||||
// write all values
|
||||
for v in self.values.iter_mut() {
|
||||
for v in record.values.iter_mut() {
|
||||
buffer.append(&mut v.data)
|
||||
}
|
||||
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)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
use byteorder::{BigEndian, ByteOrder};
|
||||
use crate::varint;
|
||||
use byteorder::{BigEndian, ByteOrder};
|
||||
|
||||
pub struct Value {
|
||||
pub datatype: Vec<u8>,
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
|
||||
impl Value {
|
||||
pub fn len(&self) -> u16 {
|
||||
(self.datatype.len() + self.data.len()) as u16
|
||||
|
|
@ -15,21 +14,33 @@ impl Value {
|
|||
|
||||
pub fn string(value: &str) -> Value {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
let mut buffer = [0_u8; 8];
|
||||
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 {
|
||||
|
|
@ -73,12 +84,7 @@ fn get_int_type(value: i64) -> Vec<u8> {
|
|||
}
|
||||
|
||||
fn get_length_of_byte_encoding(value: i64) -> u8 {
|
||||
let u =
|
||||
if value < 0 {
|
||||
!value
|
||||
} else {
|
||||
value
|
||||
};
|
||||
let u = if value < 0 { !value } else { value };
|
||||
if u <= 127 {
|
||||
1
|
||||
} else if u <= 32767 {
|
||||
|
|
@ -96,7 +102,6 @@ fn get_length_of_byte_encoding(value: i64) -> u8 {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::values::Value;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -29,13 +29,14 @@ mod tests {
|
|||
|
||||
#[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]
|
||||
fn test_write1() {
|
||||
let a:i16 = -1;
|
||||
println!("{}", a as u16);
|
||||
assert_eq!(vec![1], write(0x01));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue