Skip to content

Commit

Permalink
Add type alignment to file format
Browse files Browse the repository at this point in the history
  • Loading branch information
cberner committed Jan 15, 2023
1 parent 1ec1115 commit 1fcb6a0
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 9 deletions.
8 changes: 7 additions & 1 deletion src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,12 @@ impl<'a, K: RedbKey + ?Sized, V: RedbValue + ?Sized> TableDefinition<'a, K, V> {
///
/// ## Invariant
///
/// `name` shall not be empty.
/// `name` must not be empty.
pub const fn new(name: &'a str) -> Self {
assert!(!name.is_empty());
// Custom alignment is not currently supported
assert!(K::ALIGNMENT == 1);
assert!(V::ALIGNMENT == 1);
Self {
name,
_key_type: PhantomData,
Expand Down Expand Up @@ -102,6 +105,9 @@ pub struct MultimapTableDefinition<'a, K: RedbKey + ?Sized, V: RedbKey + ?Sized>
impl<'a, K: RedbKey + ?Sized, V: RedbKey + ?Sized> MultimapTableDefinition<'a, K, V> {
pub const fn new(name: &'a str) -> Self {
assert!(!name.is_empty());
// Custom alignment is not currently supported
assert!(K::ALIGNMENT == 1);
assert!(V::ALIGNMENT == 1);
Self {
name,
_key_type: PhantomData,
Expand Down
2 changes: 1 addition & 1 deletion src/tree_store/page_store/page_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const MIN_DESIRED_USABLE_BYTES: usize = 1024 * 1024;
const NUM_REGIONS: u32 = 1000;

// TODO: set to 1, when version 1.0 is released
pub(crate) const FILE_FORMAT_VERSION: u8 = 107;
pub(crate) const FILE_FORMAT_VERSION: u8 = 108;

fn ceil_log2(x: usize) -> usize {
if x.is_power_of_two() {
Expand Down
84 changes: 83 additions & 1 deletion src/tree_store/table_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,14 @@ impl From<u8> for TableType {
}
}

#[derive(Clone, Debug)]
#[derive(Clone, PartialEq, Debug)]
pub(crate) struct InternalTableDefinition {
table_root: Option<(PageNumber, Checksum)>,
table_type: TableType,
fixed_key_size: Option<usize>,
fixed_value_size: Option<usize>,
key_alignment: usize,
value_alignment: usize,
key_type: String,
value_type: String,
}
Expand All @@ -121,6 +123,14 @@ impl InternalTableDefinition {
self.fixed_value_size
}

pub(crate) fn get_key_alignment(&self) -> usize {
self.key_alignment
}

pub(crate) fn get_value_alignment(&self) -> usize {
self.value_alignment
}

pub(crate) fn get_type(&self) -> TableType {
self.table_type
}
Expand Down Expand Up @@ -192,6 +202,18 @@ impl RedbValue for InternalTableDefinition {
None
};
offset += size_of::<u32>();
let key_alignment = u32::from_le_bytes(
data[offset..(offset + size_of::<u32>())]
.try_into()
.unwrap(),
) as usize;
offset += size_of::<u32>();
let value_alignment = u32::from_le_bytes(
data[offset..(offset + size_of::<u32>())]
.try_into()
.unwrap(),
) as usize;
offset += size_of::<u32>();

let key_type_len = u32::from_le_bytes(
data[offset..(offset + size_of::<u32>())]
Expand All @@ -210,6 +232,8 @@ impl RedbValue for InternalTableDefinition {
table_type,
fixed_key_size,
fixed_value_size,
key_alignment,
value_alignment,
key_type,
value_type,
}
Expand Down Expand Up @@ -244,6 +268,8 @@ impl RedbValue for InternalTableDefinition {
result.push(0);
result.extend_from_slice(&[0; size_of::<u32>()])
}
result.extend_from_slice(&u32::try_from(value.key_alignment).unwrap().to_le_bytes());
result.extend_from_slice(&u32::try_from(value.value_alignment).unwrap().to_le_bytes());
result.extend_from_slice(
&u32::try_from(value.key_type.as_bytes().len())
.unwrap()
Expand Down Expand Up @@ -370,6 +396,38 @@ impl<'txn> TableTree<'txn> {
V::redb_type_name()
)));
}
if definition.get_key_alignment() != K::ALIGNMENT {
return Err(Error::Corrupted(format!(
"{:?} key alignment {} does not match {}",
name,
K::ALIGNMENT,
definition.key_alignment
)));
}
if definition.get_value_alignment() != V::ALIGNMENT {
return Err(Error::Corrupted(format!(
"{:?} value alignment {} does not match {}",
name,
V::ALIGNMENT,
definition.value_alignment
)));
}
if definition.get_fixed_key_size() != K::fixed_width() {
return Err(Error::Corrupted(format!(
"{:?} key width {:?} does not match {:?}",
name,
K::fixed_width(),
definition.get_fixed_key_size()
)));
}
if definition.get_fixed_value_size() != V::fixed_width() {
return Err(Error::Corrupted(format!(
"{:?} value width {:?} does not match {:?}",
name,
V::fixed_width(),
definition.get_fixed_value_size()
)));
}

if let Some(updated_root) = self.pending_table_updates.get(name) {
definition.table_root = *updated_root;
Expand Down Expand Up @@ -427,6 +485,8 @@ impl<'txn> TableTree<'txn> {
table_type,
fixed_key_size: K::fixed_width(),
fixed_value_size: V::fixed_width(),
key_alignment: K::ALIGNMENT,
value_alignment: V::ALIGNMENT,
key_type: K::redb_type_name(),
value_type: V::redb_type_name(),
};
Expand Down Expand Up @@ -480,3 +540,25 @@ impl<'txn> TableTree<'txn> {
})
}
}

#[cfg(test)]
mod test {
use crate::tree_store::{InternalTableDefinition, TableType};
use crate::RedbValue;

#[test]
fn round_trip() {
let x = InternalTableDefinition {
table_root: None,
table_type: TableType::Multimap,
fixed_key_size: None,
fixed_value_size: Some(5),
key_alignment: 6,
value_alignment: 7,
key_type: "Key".to_string(),
value_type: "Value".to_string(),
};
let y = InternalTableDefinition::from_bytes(InternalTableDefinition::as_bytes(&x).as_ref());
assert_eq!(x, y);
}
}
14 changes: 8 additions & 6 deletions src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ use std::fmt::Debug;
pub trait Sealed {}

pub trait RedbValue: Debug + Sealed {
const ALIGNMENT: usize = 1;

/// SelfType<'a> must be the same type as Self with all lifetimes replaced with 'a
type SelfType<'a>: Debug + 'a
where
Expand All @@ -19,6 +21,7 @@ pub trait RedbValue: Debug + Sealed {

/// Deserializes data
/// Implementations may return a view over data, or an owned type
// TODO: implement guarantee that data is aligned to Self::ALIGNMENT
fn from_bytes<'a>(data: &'a [u8]) -> Self::SelfType<'a>
where
Self: 'a;
Expand Down Expand Up @@ -74,6 +77,7 @@ impl RedbValue for () {
impl Sealed for () {}

impl<T: RedbValue> RedbValue for Option<T> {
const ALIGNMENT: usize = T::ALIGNMENT;
type SelfType<'a> = Option<T::SelfType<'a>>
where
Self: 'a;
Expand All @@ -82,7 +86,7 @@ impl<T: RedbValue> RedbValue for Option<T> {
Self: 'a;

fn fixed_width() -> Option<usize> {
T::fixed_width().map(|x| x + 1)
T::fixed_width().map(|x| x + T::ALIGNMENT)
}

fn from_bytes<'a>(data: &'a [u8]) -> Option<T::SelfType<'a>>
Expand All @@ -91,7 +95,7 @@ impl<T: RedbValue> RedbValue for Option<T> {
{
match data[0] {
0 => None,
1 => Some(T::from_bytes(&data[1..])),
1 => Some(T::from_bytes(&data[T::ALIGNMENT..])),
_ => unreachable!(),
}
}
Expand All @@ -101,12 +105,10 @@ impl<T: RedbValue> RedbValue for Option<T> {
Self: 'a,
Self: 'b,
{
let mut result = vec![];
let mut result = vec![0; T::ALIGNMENT];
if let Some(x) = value {
result.push(1);
result[0] = 1;
result.extend_from_slice(T::as_bytes(x).as_ref());
} else {
result.push(0);
}
result
}
Expand Down

0 comments on commit 1fcb6a0

Please sign in to comment.