Encapsulate header parsing logic
This commit is contained in:
parent
6cb42ec21b
commit
2c96172e9d
@ -6,6 +6,29 @@ static HEADER_STRING: &[u8] = &[
|
|||||||
83, 81, 76, 105, 116, 101, 32, 102, 111, 114, 109, 97, 116, 32, 51, 0,
|
83, 81, 76, 105, 116, 101, 32, 102, 111, 114, 109, 97, 116, 32, 51, 0,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/// A value stored as a Write Format Verson or
|
||||||
|
/// Read Format Version
|
||||||
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub enum FormatVersion {
|
||||||
|
/// Represents the rollback journal mode
|
||||||
|
Legacy,
|
||||||
|
/// Represents the Write Ahead Log mode
|
||||||
|
WriteAheadLog,
|
||||||
|
/// Represents any mode not 1 or 2, the value
|
||||||
|
/// will be provided
|
||||||
|
Unknown(u8),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u8> for FormatVersion {
|
||||||
|
fn from(v: u8) -> Self {
|
||||||
|
match v {
|
||||||
|
1 => Self::Legacy,
|
||||||
|
2 => Self::WriteAheadLog,
|
||||||
|
_ => Self::Unknown(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// This struct will wrap our 32bit number allowing us
|
/// This struct will wrap our 32bit number allowing us
|
||||||
/// to control how it gets used later.
|
/// to control how it gets used later.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -39,12 +62,27 @@ impl TryFrom<u16> for PageSize {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parse_header(bytes: &[u8]) -> Result<(PageSize, FormatVersion, FormatVersion), Error> {
|
||||||
|
// Check that the first 16 bytes match the header string
|
||||||
|
validate_magic_string(&bytes)?;
|
||||||
|
// capture the page size
|
||||||
|
let page_size = parse_page_size(bytes)?;
|
||||||
|
// capture the write format version
|
||||||
|
let write_version = FormatVersion::from(bytes[18]);
|
||||||
|
// capture the read format version
|
||||||
|
let read_version = FormatVersion::from(bytes[19]);
|
||||||
|
|
||||||
|
Ok((page_size, write_version, read_version))
|
||||||
|
}
|
||||||
|
|
||||||
/// Validate that the bytes provided match the special string
|
/// Validate that the bytes provided match the special string
|
||||||
/// at the start of Sqlite3 files
|
/// at the start of Sqlite3 files
|
||||||
pub fn validate_magic_string(bytes: &[u8]) -> Result<(), Error> {
|
pub fn validate_magic_string(bytes: &[u8]) -> Result<(), Error> {
|
||||||
let buf = &bytes[0..16];
|
let buf = &bytes[0..16];
|
||||||
if buf != HEADER_STRING {
|
if buf != HEADER_STRING {
|
||||||
return Err(Error::HeaderString(String::from_utf8_lossy(buf).to_string()));
|
return Err(Error::HeaderString(
|
||||||
|
String::from_utf8_lossy(buf).to_string(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -54,7 +92,7 @@ pub fn validate_magic_string(bytes: &[u8]) -> Result<(), Error> {
|
|||||||
pub fn parse_page_size(bytes: &[u8]) -> Result<PageSize, Error> {
|
pub fn parse_page_size(bytes: &[u8]) -> Result<PageSize, Error> {
|
||||||
// Convert into array, and convert error if needed, so that the `?` operator works
|
// Convert into array, and convert error if needed, so that the `?` operator works
|
||||||
let page_size_bytes: [u8; 2] = bytes[16..18].try_into().map_err(|_| {
|
let page_size_bytes: [u8; 2] = bytes[16..18].try_into().map_err(|_| {
|
||||||
Error::InvalidPageSize(format!("expected a 2 byte slice, found: {:?}", bytes))
|
Error::InvalidPageSize(format!("expected a 2 byte slice, found: {:?}", bytes))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
u16::from_be_bytes(page_size_bytes).try_into()
|
u16::from_be_bytes(page_size_bytes).try_into()
|
||||||
|
11
src/main.rs
11
src/main.rs
@ -1,5 +1,5 @@
|
|||||||
use sqlite_parser::{
|
use sqlite_parser::{
|
||||||
header::{validate_magic_string, parse_page_size},
|
header::parse_header,
|
||||||
error::Error,
|
error::Error,
|
||||||
};
|
};
|
||||||
use std::fs::read;
|
use std::fs::read;
|
||||||
@ -7,13 +7,12 @@ use std::fs::read;
|
|||||||
fn main() -> Result<(), Error> {
|
fn main() -> Result<(), Error> {
|
||||||
// first, read in all the bytes of our file
|
// first, read in all the bytes of our file
|
||||||
// using unwrap to just panic if this fails
|
// using unwrap to just panic if this fails
|
||||||
let contents = read("data.sqlite").unwrap();
|
let contents = read("data.sqlite")
|
||||||
|
.expect("Failed to read data.sqlite");
|
||||||
|
|
||||||
validate_magic_string(&contents)?;
|
let (page_size, write_format, read_format) = parse_header(&contents[0..100])?;
|
||||||
|
|
||||||
let page_size = parse_page_size(&contents)?;
|
println!("page_size {:?}, write_format {:?}, read_format {:?}", page_size, write_format, read_format);
|
||||||
|
|
||||||
println!("{:?}", page_size);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user