sqlite-parser/src/header.rs

61 lines
1.9 KiB
Rust

use crate::error::Error;
use std::convert::{TryFrom, TryInto};
static HEADER_STRING: &[u8] = &[
//S q l i t e ` ` f o r m a t ` ` 3 \u{0}
83, 81, 76, 105, 116, 101, 32, 102, 111, 114, 109, 97, 116, 32, 51, 0,
];
/// This struct will wrap our 32bit number allowing us
/// to control how it gets used later.
#[derive(Debug)]
pub struct PageSize(u32);
impl TryFrom<u16> for PageSize {
type Error = Error;
fn try_from(v: u16) -> Result<PageSize, Self::Error> {
match v {
// Special case for the largest page size
1 => Ok(PageSize(65_536u32)),
// < 512, != 1
0 | 2..=511 => Err(Error::InvalidPageSize(format!(
"value must be >= 512, found: {}",
v
))),
_ => {
if v.is_power_of_two() {
Ok(PageSize(v as u32))
} else {
Err(Error::InvalidPageSize(format!(
"value must be a power of 2, found: {}",
v
)))
}
}
}
}
}
/// Validate that the bytes provided match the special string
/// at the start of Sqlite3 files
pub fn validate_magic_string(bytes: &[u8]) -> Result<(), Error> {
let buf = &bytes[0..16];
if buf != HEADER_STRING {
return Err(Error::HeaderString(String::from_utf8_lossy(buf).to_string()));
}
Ok(())
}
/// Attempt to generate the appropriate `PageSize` struct
pub fn parse_page_size(bytes: &[u8]) -> Result<PageSize, Error> {
// 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(|_| {
Error::InvalidPageSize(format!("expected a 2 byte slice, found: {:?}", bytes))
})?;
u16::from_be_bytes(page_size_bytes).try_into()
}