pub mod builder;
mod entry_store;
mod index;
pub mod layout;
mod lazy_entry;
mod property_compare;
mod range;
mod raw_layout;
mod raw_value;
mod value_store;
use self::index::IndexHeader;
use crate::bases::*;
use crate::common::{CheckInfo, DirectoryPackHeader, Pack, PackHeader, PackKind};
use std::sync::{Arc, RwLock};
use uuid::Uuid;
pub use self::entry_store::EntryStore;
pub use self::index::Index;
pub use self::range::{CompareTrait, RangeTrait};
pub(crate) use self::value_store::{ValueStore, ValueStoreTrait};
pub use lazy_entry::LazyEntry;
pub use raw_value::RawValue;
pub trait EntryTrait {
fn get_variant_id(&self) -> Result<Option<VariantIdx>>;
fn get_value(&self, name: impl AsRef<str>) -> Result<Option<RawValue>>;
}
mod private {
use super::*;
pub trait ValueStorageTrait {
type ValueStore: ValueStoreTrait + 'static;
fn get_value_store(&self, id: ValueStoreIdx) -> Result<Arc<Self::ValueStore>>;
}
}
pub struct ValueStorage(VecCache<ValueStore, DirectoryPack>);
impl ValueStorage {
fn new(source: Arc<DirectoryPack>) -> Self {
Self(VecCache::new(source))
}
}
impl private::ValueStorageTrait for ValueStorage {
type ValueStore = ValueStore;
fn get_value_store(&self, store_id: ValueStoreIdx) -> Result<Arc<Self::ValueStore>> {
Ok(Arc::clone(self.0.get(store_id)?))
}
}
pub struct EntryStorage(VecCache<EntryStore, DirectoryPack>);
impl EntryStorage {
fn new(source: Arc<DirectoryPack>) -> Self {
Self(VecCache::new(source))
}
pub fn get_entry_store(&self, store_id: EntryStoreIdx) -> Result<&Arc<EntryStore>> {
self.0.get(store_id)
}
}
pub struct DirectoryPack {
pack_header: PackHeader,
header: DirectoryPackHeader,
value_stores_ptrs: ArrayReader<SizedOffset, u8>,
entry_stores_ptrs: ArrayReader<SizedOffset, u32>,
index_ptrs: ArrayReader<SizedOffset, u32>,
reader: Reader,
check_info: RwLock<Option<CheckInfo>>,
}
impl DirectoryPack {
pub fn new(reader: Reader) -> Result<DirectoryPack> {
let reader = reader.cut(Offset::zero(), reader.size(), true)?;
let pack_header = reader.parse_block_at::<PackHeader>(Offset::zero())?;
if pack_header.magic != PackKind::Directory {
return Err(format_error!("Pack Magic is not DirectoryPack"));
}
let header =
reader.parse_block_at::<DirectoryPackHeader>(Offset::from(PackHeader::BLOCK_SIZE))?;
let value_stores_ptrs = ArrayReader::new_memory_from_reader(
&reader,
header.value_store_ptr_pos,
*header.value_store_count,
)?;
let entry_stores_ptrs = ArrayReader::new_memory_from_reader(
&reader,
header.entry_store_ptr_pos,
*header.entry_store_count,
)?;
let index_ptrs = ArrayReader::new_memory_from_reader(
&reader,
header.index_ptr_pos,
*header.index_count,
)?;
Ok(DirectoryPack {
pack_header,
header,
value_stores_ptrs,
entry_stores_ptrs,
index_ptrs,
reader,
check_info: RwLock::new(None),
})
}
pub fn get_free_data(&self) -> &[u8] {
self.header.free_data.as_ref()
}
pub fn get_index(&self, index_id: IndexIdx) -> Result<Index> {
let sized_offset = self.index_ptrs.index(*index_id)?;
let index_header = self
.reader
.parse_block_in::<IndexHeader>(sized_offset.offset, sized_offset.size)?;
let index = Index::new(index_header);
Ok(index)
}
pub fn get_index_from_name(&self, index_name: &str) -> Result<Option<Index>> {
for index_id in self.header.index_count {
let sized_offset = self.index_ptrs.index(*index_id)?;
let index_header = self
.reader
.parse_block_in::<IndexHeader>(sized_offset.offset, sized_offset.size)?;
if index_header.name.as_str() == index_name {
let index = Index::new(index_header);
return Ok(Some(index));
}
}
Ok(None)
}
pub fn create_value_storage(self: &Arc<Self>) -> Arc<ValueStorage> {
Arc::new(ValueStorage::new(Arc::clone(self)))
}
pub fn create_entry_storage(self: &Arc<Self>) -> Arc<EntryStorage> {
Arc::new(EntryStorage::new(Arc::clone(self)))
}
}
impl CachableSource<ValueStore> for DirectoryPack {
type Error = Error;
type Idx = ValueStoreIdx;
fn get_len(&self) -> usize {
self.header.value_store_count.into_usize()
}
fn get_value(&self, id: Self::Idx) -> Result<Arc<ValueStore>> {
let sized_offset = self.value_stores_ptrs.index(*id)?;
Ok(Arc::new(
self.reader.parse_data_block::<ValueStore>(sized_offset)?,
))
}
}
impl CachableSource<EntryStore> for DirectoryPack {
type Error = Error;
type Idx = EntryStoreIdx;
fn get_len(&self) -> usize {
self.header.entry_store_count.into_usize()
}
fn get_value(&self, id: Self::Idx) -> Result<Arc<EntryStore>> {
let sized_offset = self.entry_stores_ptrs.index(*id)?;
Ok(Arc::new(
self.reader.parse_data_block::<EntryStore>(sized_offset)?,
))
}
}
impl Pack for DirectoryPack {
fn kind(&self) -> PackKind {
self.pack_header.magic
}
fn app_vendor_id(&self) -> VendorId {
self.pack_header.app_vendor_id
}
fn version(&self) -> (u8, u8) {
(
self.pack_header.major_version,
self.pack_header.minor_version,
)
}
fn uuid(&self) -> Uuid {
self.pack_header.uuid
}
fn size(&self) -> Size {
self.pack_header.file_size
}
fn check(&self) -> Result<bool> {
if self.check_info.read().unwrap().is_none() {
let check_info = self.reader.parse_block_in::<CheckInfo>(
self.pack_header.check_info_pos,
self.pack_header.check_info_size(),
)?;
let mut s_check_info = self.check_info.write().unwrap();
*s_check_info = Some(check_info);
}
let mut check_stream = self.reader.create_stream(
Offset::zero(),
Size::from(self.pack_header.check_info_pos),
false,
)?;
Ok(self
.check_info
.read()
.unwrap()
.unwrap()
.check(&mut check_stream)?)
}
}
#[cfg(feature = "explorable_serde")]
impl serde::Serialize for DirectoryPack {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeStruct;
let mut cont = serializer.serialize_struct("DirectoryPack", 5)?;
cont.serialize_field("uuid", &self.uuid())?;
cont.serialize_field(
"indexes",
&self
.header
.index_count
.into_iter()
.map(|c| {
let sized_offset = self.index_ptrs.index(*c).unwrap();
let index_header = self
.reader
.parse_block_in::<IndexHeader>(sized_offset.offset, sized_offset.size)
.unwrap();
Index::new(index_header)
})
.collect::<Vec<_>>(),
)?;
cont.serialize_field(
"entry_stores",
&self
.header
.entry_store_count
.into_iter()
.map(|c| {
let sized_offset = self.entry_stores_ptrs.index(*c).unwrap();
self.reader
.parse_data_block::<EntryStore>(sized_offset)
.unwrap()
})
.collect::<Vec<_>>(),
)?;
cont.serialize_field(
"value_stores",
&self
.header
.value_store_count
.into_iter()
.map(|c| {
let sized_offset = self.value_stores_ptrs.index(*c).unwrap();
self.reader
.parse_data_block::<ValueStore>(sized_offset)
.unwrap()
})
.collect::<Vec<_>>(),
)?;
cont.serialize_field("free_data", &self.header.free_data)?;
cont.end()
}
}
#[cfg(feature = "explorable")]
struct VecWithKeyWrapper<'a, T> {
letter: &'a str,
data: &'a [T],
}
#[cfg(feature = "explorable")]
impl<T: graphex::Display> graphex::Display for VecWithKeyWrapper<'_, T> {
fn print_content(&self, out: &mut graphex::Output) -> graphex::Result {
use yansi::Paint;
for (i, d) in self.data.iter().enumerate() {
out.field(&format!("{}.{i}", self.letter).bold(), d)?;
}
Ok(())
}
}
#[cfg(feature = "explorable")]
impl graphex::Display for DirectoryPack {
fn header_footer(&self) -> Option<(String, String)> {
Some(("DirectoryPack(".to_string(), ")".to_string()))
}
fn print_content(&self, out: &mut graphex::Output) -> graphex::Result {
out.field("uuid", &self.uuid().to_string())?;
out.field(
"indexes",
&self
.header
.index_count
.into_iter()
.map(|c| {
let sized_offset = self.index_ptrs.index(*c).unwrap();
let index_header = self
.reader
.parse_block_in::<IndexHeader>(sized_offset.offset, sized_offset.size)
.unwrap();
Index::new(index_header)
})
.collect::<Vec<_>>(),
)?;
out.field(
"entry_stores",
&VecWithKeyWrapper {
letter: "e",
data: &self
.header
.entry_store_count
.into_iter()
.map(|c| {
let sized_offset = self.entry_stores_ptrs.index(*c).unwrap();
self.reader
.parse_data_block::<EntryStore>(sized_offset)
.unwrap()
})
.collect::<Vec<_>>(),
},
)?;
out.field(
"value_stores",
&VecWithKeyWrapper {
letter: "v",
data: &self
.header
.value_store_count
.into_iter()
.map(|c| {
let sized_offset = self.value_stores_ptrs.index(*c).unwrap();
self.reader
.parse_data_block::<ValueStore>(sized_offset)
.unwrap()
})
.collect::<Vec<_>>(),
},
)?;
out.field("free_data", &self.header.free_data)
}
}
#[cfg(feature = "explorable")]
impl graphex::Node for DirectoryPack {
fn next(&self, key: &str) -> graphex::ExploreResult<'_> {
if let Some(item) = key.strip_prefix("e.") {
let index = if let Ok(index) = item.parse::<u32>() {
EntryStoreIdx::from(index)
} else {
let index = self.get_index_from_name(item)?.ok_or_else(|| {
graphex::Error::key(&format!("Key {item} not found in directory pack."))
})?;
index.get_store_id()
};
let sized_offset = self.entry_stores_ptrs.index(*index)?;
Ok(Box::new(self.reader.parse_data_block::<EntryStore>(sized_offset)?).into())
} else if let Some(item) = key.strip_prefix("v.") {
let index = item
.parse::<u8>()
.map_err(|e| graphex::Error::key(&format!("{e}")))?;
let sized_offset = self.value_stores_ptrs.index(index.into())?;
Ok(Box::new(self.reader.parse_data_block::<ValueStore>(sized_offset)?).into())
} else {
Err(graphex::Error::key(key))
}
}
fn display(&self) -> &dyn graphex::Display {
self
}
#[cfg(feature = "explorable_serde")]
fn serde(&self) -> Option<&dyn erased_serde::Serialize> {
Some(self)
}
}
#[cfg(test)]
mod tests {
use super::raw_value::*;
use super::*;
use crate::common::ContentAddress;
#[derive(Debug)]
struct FakeArray {
size: Option<ASize>,
base: BaseArray,
base_len: u8,
extend: Option<ValueIdx>,
}
impl FakeArray {
fn new(
size: Option<ASize>,
base: BaseArray,
base_len: u8,
extend: Option<ValueIdx>,
) -> Self {
Self {
size,
base,
base_len,
extend,
}
}
}
impl PartialEq<Array> for FakeArray {
fn eq(&self, other: &Array) -> bool {
let base = self.size == other.size
&& self.base == other.base
&& self.base_len == other.base_len;
if !base {
return false;
}
if self.extend.is_some() != other.extend.is_some() {
return false;
}
if self.extend.is_none() {
return true;
}
self.extend.unwrap() == other.extend.as_ref().unwrap().value_id
}
}
#[rustest::test]
fn test_directorypack() -> rustest::Result {
let mut content = vec![
0x6a, 0x62, 0x6b, 0x64, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
0x0e, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAE, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
content.extend_from_slice(&[0x77, 0x41, 0x69, 0x29]);
content.extend_from_slice(&[
0x3D, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, ]);
content.extend_from_slice(&[0xff; 27]); content.extend_from_slice(&[0xD9, 0xA4, 0x04, 0x38]);
content.extend_from_slice(&[
b'H', b'e', b'l', b'l', b'o', b'F', b'o', b'o', b'J', 0xc5, 0xab, b'b', b'a', b'k', b'o', ]);
content.extend_from_slice(&[0x71, 0x51, 0xDF, 0x1D]);
content.extend_from_slice(&[
0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0f, 0x05, 0x08, ]);
content.extend_from_slice(&[0x1E, 0x6E, 0xE7, 0xB7]);
content.extend_from_slice(&[
13, 0x00, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, ]);
content.extend_from_slice(&[0xE0, 0x14, 0x59, 0xCA]);
#[rustfmt::skip]
content.extend_from_slice(&[
0x05, 0x00, 0x05, b'A', b'B', 0x01, 0x13, 0x12, 0x11, 0x00, 0x00, 0x00, 0x00, 0x07, 0x02, 0x07, b'a', b'B', 0x00, 0x23, 0x22, 0x21, 0x01, 0x00, 0x00, 0x00, 0x03, 0x01, 0x09, b'A', b'B', 0x02, 0x33, 0x32, 0x31, 0x00, 0x01, 0x00, 0x00, 0x07, 0x02, 0x05, b'A', b'B', 0x01, 0x43, 0x42, 0x41, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x00, 0x01, 0x53, 0x52, 0x51, 0x00, 0xaa, 0xaa, 0xaa, ]);
content.extend_from_slice(&[0x4C, 0x67, 0x87, 0x9B]);
#[rustfmt::skip]
content.extend_from_slice(&[
0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x04, 0b0101_0001, 0b001_00000, 0x00, 1, b'A', 0b0101_0001, 0b001_00010, 0x00, 1, b'B', 0b0010_0010, 1, b'C', 0b0001_0010, 1, b'D', ]);
content.extend_from_slice(&[0x49, 0x82, 0x74, 0xD6]);
content.extend_from_slice(&[
26, 0x00, 0xF5, 0x00, 0x00, 0x00, 0x00, 0x00, ]);
content.extend_from_slice(&[0x03, 0x73, 0x94, 0x0B]);
content.extend_from_slice(&[
0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, b'm', b'y', b' ', b'i', b'n', b'd', b'e', b'x', ]);
content.extend_from_slice(&[0x86, 0x5C, 0x21, 0xDF]);
content.extend_from_slice(&[
26, 0x00, 0x1F, 0x01, 0x00, 0x00, 0x00, 0x00, ]);
content.extend_from_slice(&[0x27, 0x31, 0x53, 0x6F]);
let hash = blake3::hash(&content);
content.push(0x01); content.extend(hash.as_bytes());
content.extend_from_slice(&[0x72, 0x28, 0x30, 0x8F]);
let mut footer = [0; 64];
footer.copy_from_slice(&content[..64]);
footer.reverse();
content.extend_from_slice(&footer);
let directory_pack = Arc::new(DirectoryPack::new(content.into())?);
assert!(directory_pack.check()?);
let index = directory_pack.get_index(0.into())?;
let value_storage = directory_pack.create_value_storage();
let entry_storage = directory_pack.create_entry_storage();
let builder =
builder::AnyBuilder::new(index.get_store(&entry_storage)?, value_storage.as_ref())?;
assert_eq!(index.count(), 4.into());
{
let entry = index.get_entry(&builder, 0.into())?.unwrap();
assert_eq!(entry.get_variant_id()?, None);
let value0 = entry.get_value("A")?;
if let Some(RawValue::Array(a)) = &value0 {
assert_eq!(
&FakeArray::new(
Some(ASize::new(7)),
BaseArray::default(),
0,
Some(ValueIdx::from(2))
),
a
);
} else {
panic!("Must be a array");
};
assert_eq!(
value0.unwrap().as_vec()?,
b"J\xc5\xabbako" );
let value1 = entry.get_value("B")?;
if let Some(RawValue::Array(a)) = &value1 {
assert_eq!(
&FakeArray::new(
Some(ASize::new(7)),
BaseArray::new(b"aB"),
2,
Some(ValueIdx::from(0))
),
a
);
} else {
panic!("Must be a array");
};
assert_eq!(value1.unwrap().as_vec()?, b"aBHello");
assert_eq!(entry.get_value("C")?, Some(RawValue::U32(0x212223)));
assert_eq!(
entry.get_value("D")?,
Some(RawValue::Content(ContentAddress {
pack_id: PackId::from(1),
content_id: ContentIdx::from(0)
}))
);
}
{
let entry = index.get_entry(&builder, 1.into())?.unwrap();
assert_eq!(entry.get_variant_id()?, None);
let value0 = entry.get_value("A")?;
if let Some(RawValue::Array(a)) = &value0 {
assert_eq!(
&FakeArray::new(
Some(ASize::new(3)),
BaseArray::default(),
0,
Some(ValueIdx::from(1))
),
a
);
} else {
panic!("Must be a array");
};
assert_eq!(value0.unwrap().as_vec()?, b"Foo");
let value1 = entry.get_value("B")?;
if let Some(RawValue::Array(a)) = &value1 {
assert_eq!(
&FakeArray::new(
Some(ASize::new(9)),
BaseArray::new(b"AB"),
2,
Some(ValueIdx::from(2))
),
a
);
} else {
panic!("Must be a array");
};
assert_eq!(value1.unwrap().as_vec()?, b"ABJ\xc5\xabbako");
assert_eq!(entry.get_value("C")?, Some(RawValue::U32(0x313233)));
assert_eq!(
entry.get_value("D")?,
Some(RawValue::Content(ContentAddress {
pack_id: PackId::from(0),
content_id: ContentIdx::from(1)
}))
);
}
{
let entry = index.get_entry(&builder, 2.into())?.unwrap();
assert_eq!(entry.get_variant_id()?, None);
let value0 = entry.get_value("A")?;
if let Some(RawValue::Array(a)) = &value0 {
assert_eq!(
&FakeArray::new(
Some(ASize::new(7)),
BaseArray::default(),
0,
Some(ValueIdx::from(2))
),
a
);
} else {
panic!("Must be a array");
};
assert_eq!(value0.unwrap().as_vec()?, b"J\xc5\xabbako");
let value1 = entry.get_value("B")?;
if let Some(RawValue::Array(a)) = &value1 {
assert_eq!(
&FakeArray::new(
Some(ASize::new(5)),
BaseArray::new(b"AB"),
2,
Some(ValueIdx::from(1))
),
a
);
} else {
panic!("Must be a array");
};
assert_eq!(value1.unwrap().as_vec()?, b"ABFoo");
assert_eq!(entry.get_value("C")?, Some(RawValue::U32(0x414243)));
assert_eq!(
entry.get_value("D")?,
Some(RawValue::Content(ContentAddress {
pack_id: PackId::from(0),
content_id: ContentIdx::from(2)
}))
);
}
{
let entry = index.get_entry(&builder, 3.into())?.unwrap();
assert_eq!(entry.get_variant_id()?, None);
let value0 = entry.get_value("A")?;
if let Some(RawValue::Array(a)) = &value0 {
assert_eq!(
&FakeArray::new(
Some(ASize::new(5)),
BaseArray::default(),
0,
Some(ValueIdx::from(0))
),
a
);
} else {
panic!("Must be a array");
};
assert_eq!(value0.unwrap().as_vec()?, b"Hello");
let value1 = entry.get_value("B")?;
if let Some(RawValue::Array(a)) = &value1 {
assert_eq!(
&FakeArray::new(
Some(ASize::new(5)),
BaseArray::default(),
2,
Some(ValueIdx::from(1))
),
a
);
} else {
panic!("Must be a array");
};
assert_eq!(value1.unwrap().as_vec()?, b"\0\0Foo");
assert_eq!(entry.get_value("C")?, Some(RawValue::U32(0x515253)));
assert_eq!(
entry.get_value("D")?,
Some(RawValue::Content(ContentAddress {
pack_id: PackId::from(0),
content_id: ContentIdx::from(0xaaaaaa)
}))
);
}
Ok(())
}
}