use crate::error::ResourceStorageError;
use crate::storage::ResourceStorage;
use std::fmt::Debug;
use std::rc::Rc;
pub use std::marker;
pub trait Ref: Clone + Copy + Debug + PartialEq {}
pub trait RefMut: Debug {}
pub trait Struct<'a>: Clone {
const SCHEMA: &'static str;
const SIZE_IN_BYTES: usize;
type Item: Ref;
fn create(data: &'a [u8]) -> Self::Item;
type ItemMut: RefMut;
fn create_mut(data: &'a mut [u8]) -> Self::ItemMut;
}
pub trait IndexStruct<'a>: Struct<'a> {
fn index(data: Self::Item) -> usize;
fn set_index(data: Self::ItemMut, value: usize);
}
pub trait Index: Ref {}
pub trait IndexMut: RefMut {}
pub type TypeIndex = u8;
pub trait VariadicRef: Clone + Debug + PartialEq {
fn size_in_bytes(&self) -> usize;
}
pub trait VariadicStruct<'a>: Clone {
type Item: VariadicRef;
fn create(data: TypeIndex, _: &'a [u8]) -> Self::Item;
type ItemMut;
fn create_mut(data: &'a mut Vec<u8>) -> Self::ItemMut;
}
pub trait Archive: Debug + Clone {
const NAME: &'static str;
const SCHEMA: &'static str;
fn open(storage: Rc<ResourceStorage>) -> Result<Self, ResourceStorageError>;
}
pub trait ArchiveBuilder: Clone {
const NAME: &'static str;
const SCHEMA: &'static str;
fn new(storage: Rc<ResourceStorage>) -> Result<Self, ResourceStorageError>;
}
#[macro_export]
macro_rules! define_struct {
($factory:ident, $name:ident, $name_mut:ident, $schema:expr, $size_in_bytes:expr
$(,($field:ident, $field_setter:ident, $type:tt, $offset:expr, $bit_size:expr))*) => {
define_struct!($factory, $name, $name_mut, $schema, $size_in_bytes
$(,($field, $field_setter, $type: $type, $offset, $bit_size))*
);
};
($factory:ident, $name:ident, $name_mut:ident, $schema:expr, $size_in_bytes:expr
$(,($field:ident, $field_setter:ident, $type:tt: $primitive_type:tt, $offset:expr, $bit_size:expr))*) =>
{
#[derive(Clone, Copy)]
pub struct $name<'a> {
data: *const u8,
_phantom: $crate::marker::PhantomData<&'a u8>,
}
#[derive(Clone)]
pub struct $factory{}
impl<'a> $crate::Struct<'a> for $factory
{
const SCHEMA: &'static str = $schema;
const SIZE_IN_BYTES: usize = $size_in_bytes;
type Item = $name<'a>;
#[inline]
fn create(data : &'a[u8]) -> Self::Item
{
Self::Item{ data : data.as_ptr(), _phantom : $crate::marker::PhantomData }
}
type ItemMut = $name_mut<'a>;
#[inline]
fn create_mut(data: &'a mut[u8]) -> Self::ItemMut
{
Self::ItemMut{ data : data.as_mut_ptr(), _phantom : $crate::marker::PhantomData }
}
}
impl<'a> $name<'a> {
#[inline]
$(pub fn $field(&self) -> $type {
let value = read_bytes!($primitive_type, self.data, $offset, $bit_size);
unsafe { ::std::mem::transmute::<$primitive_type, $type>(value) }
})*
}
impl<'a> ::std::fmt::Debug for $name<'a> {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f,
concat!(stringify!($factory), " {{ ",
intersperse!($(concat!( stringify!($field), ": {:?}")), *), " }}"),
$(self.$field(),)*)
}
}
impl<'a> ::std::cmp::PartialEq for $name<'a> {
#[inline]
fn eq(&self, other: &$name) -> bool {
$(self.$field() == other.$field()) && *
}
}
impl<'a> $crate::Ref for $name<'a> {}
pub struct $name_mut<'a> {
data: *mut u8,
_phantom: $crate::marker::PhantomData<&'a u8>,
}
impl<'a> $name_mut<'a> {
#[inline]
$(pub fn $field(&self) -> $type {
let value = read_bytes!($primitive_type, self.data, $offset, $bit_size);
unsafe { ::std::mem::transmute::<$primitive_type, $type>(value) }
})*
#[inline]
$(pub fn $field_setter(&mut self, value: $type) {
let buffer = unsafe {
::std::slice::from_raw_parts_mut(self.data, $size_in_bytes)
};
write_bytes!($type; value, buffer, $offset, $bit_size)
})*
#[inline]
pub fn fill_from(&mut self, other: &$name) {
$(self.$field_setter(other.$field());)*
}
#[inline]
pub fn into_ref(self) -> $name<'a> {
$name{ data : self.data, _phantom : $crate::marker::PhantomData }
}
}
impl<'a> ::std::fmt::Debug for $name_mut<'a> {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
$name{ data : self.data, _phantom : $crate::marker::PhantomData }.fmt( f )
}
}
impl<'a> $crate::RefMut for $name_mut<'a> {}
};
}
#[macro_export]
macro_rules! define_index {
($factory:ident,$name:ident, $name_mut:ident, $schema:expr, $size_in_bytes:expr, $size_in_bits:expr) => {
define_struct!(
$factory,
$name,
$name_mut,
$schema,
$size_in_bytes,
(value, set_value, u64, 0, $size_in_bits)
);
impl<'a> $crate::IndexStruct<'a> for $factory {
#[inline]
fn index(data: Self::Item) -> usize {
data.value() as usize
}
#[inline]
fn set_index(mut data: Self::ItemMut, value: usize) {
data.set_value(value as u64);
}
}
};
}
#[macro_export]
macro_rules! define_variadic_struct {
($factory:ident, $name:ident, $item_builder_name:ident, $index_type:path,
$($type_index:expr => ($type:tt, $add_type_fn:ident)),+) =>
{
#[derive(Clone, PartialEq)]
pub enum $name<'a> {
$($type(<$type as $crate::Struct<'a>>::Item),)*
}
impl<'a> ::std::fmt::Debug for $name<'a> {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
match *self {
$($name::$type(ref inner) => write!(f, "{:?}", inner)),+
}
}
}
impl<'a> $crate::VariadicRef for $name<'a> {
#[inline]
fn size_in_bytes(&self) -> usize {
match *self {
$($name::$type(_) => <$type as $crate::Struct<'a>>::SIZE_IN_BYTES),+
}
}
}
pub struct $item_builder_name<'a> {
data: &'a mut Vec<u8>
}
impl<'a> $item_builder_name<'a> {
#[inline]
$(pub fn $add_type_fn<'b>(&'b mut self) -> <$type as $crate::Struct<'b>>::ItemMut {
let old_len = self.data.len();
let increment = 1 + <$type as $crate::Struct<'b>>::SIZE_IN_BYTES;
self.data.resize(old_len + increment, 0);
self.data[old_len - $crate::PADDING_SIZE] = $type_index;
<$type as $crate::Struct<'b>>::create_mut(
&mut self.data[1 + old_len - $crate::PADDING_SIZE..]
)
})*
}
#[derive(Clone)]
pub struct $factory{}
impl<'a> $crate::VariadicStruct<'a> for $factory {
type Item = $name<'a>;
#[inline]
fn create(index : $crate::TypeIndex, data : &'a [u8]) -> Self::Item
{
match index {
$($type_index => $name::$type(<$type as $crate::Struct<'a>>::create(data))),+,
_ => panic!(concat!(
"invalid type index {} for type ", stringify!($name)), index),
}
}
type ItemMut = $item_builder_name<'a>;
#[inline]
fn create_mut(data : &'a mut Vec<u8>) -> Self::ItemMut
{
$item_builder_name{data}
}
}
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! opt {
($type:ty, false) => {
$type
};
($type:ty, true) => {
Option<$type>
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! static_if {
(true, $true_block:block, $false_block:block) => {
$true_block
};
(false, $true_block:block, $false_block:block) => {
$false_block
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! check_resource {
($res:expr,false) => {
$res?
};
($res:expr,true) => {
$res.ok()
};
}
#[macro_export]
macro_rules! define_archive {
($name:ident, $builder_name:ident, $archive_schema:expr;
$(($struct_resource:ident, $struct_setter:ident, $struct_type:tt, $struct_schema:expr,
$is_optional_struct:ident)),*;
$(($vector_resource:ident, $vector_setter:ident, $vector_start:ident,
$element_type:tt, $element_schema:expr, $is_optional_vector:ident)),*;
$(($multivector_resource:ident,
$multivector_start:ident,
$variadic_type:tt, $variadic_type_schema:expr,
$multivector_resource_index:ident, $index_type:path,
$is_optional_multivector:ident)),*;
$(($raw_data_resource:ident, $raw_data_resource_setter:ident,
$raw_data_schema:expr, $is_optional_raw_data:ident)),*;
$(($subarchive_resource:ident,
$subarchive_type:tt, $subarchive_builder_type:tt, $subarchive_schema:expr,
$is_optional_subarchive:ident)),*
) => {
#[derive(Clone)]
pub struct $name {
_storage: ::std::rc::Rc<$crate::ResourceStorage>
$(,$struct_resource: opt!($crate::MemoryDescriptor, $is_optional_struct))*
$(,$vector_resource: opt!($crate::MemoryDescriptor, $is_optional_vector))*
$(,$multivector_resource: (
opt!($crate::MemoryDescriptor, $is_optional_multivector),
opt!($crate::MemoryDescriptor, $is_optional_multivector)))*
$(,$raw_data_resource: opt!($crate::MemoryDescriptor, $is_optional_raw_data))*
$(,$subarchive_resource: opt!($subarchive_type, $is_optional_subarchive))*
}
impl $name {
fn read_resource<R>(
storage: &$crate::ResourceStorage,
name: &str,
schema: &str,
) -> Result<R, $crate::ResourceStorageError>
where
R: From<$crate::MemoryDescriptor>,
{
storage.read(name, schema).map(|x|R::from($crate::MemoryDescriptor::new(&x)))
}
$(pub fn $struct_resource(&self) -> opt!(
<$struct_type as $crate::Struct>::Item, $is_optional_struct)
{
static_if!($is_optional_struct, {
self.$struct_resource.as_ref().map(|mem_desc| {
<$struct_type as $crate::Struct>::create(&unsafe{mem_desc.as_bytes()})
})
}, {
<$struct_type as $crate::Struct>::create(&unsafe{self.$struct_resource.as_bytes()})
})
})*
$(pub fn $vector_resource(&self) -> opt!(
$crate::ArrayView<$element_type>, $is_optional_vector)
{
static_if!($is_optional_vector, {
self.$vector_resource.as_ref().map(|x|$crate::ArrayView::new(unsafe{x.as_bytes()}))
}, {
$crate::ArrayView::new(&unsafe{self.$vector_resource.as_bytes()})
})
})*
$(pub fn $multivector_resource(&self) -> opt!(
$crate::MultiArrayView<$index_type, $variadic_type>, $is_optional_multivector)
{
static_if!($is_optional_multivector, {
let index_mem_desc = &self.$multivector_resource.0.as_ref();
let res_mem_desc = &self.$multivector_resource.1.as_ref();
index_mem_desc
.map(|x|$crate::ArrayView::new(unsafe{x.as_bytes()}))
.and_then(move |index| {
res_mem_desc.map(move |mem_desc| {
$crate::MultiArrayView::new(index, unsafe{mem_desc.as_bytes()})
})
})
}, {
$crate::MultiArrayView::new(
$crate::ArrayView::new(&unsafe{self.$multivector_resource.0.as_bytes()}),
&unsafe{self.$multivector_resource.1.as_bytes()},
)
})
})*
$(pub fn $raw_data_resource(&self) -> opt!(&[u8], $is_optional_raw_data) {
static_if!($is_optional_raw_data, {
self.$raw_data_resource.as_ref().map(|mem_desc| {
unsafe { mem_desc.as_bytes() }
})
}, {
unsafe {
self.$raw_data_resource.as_bytes()
}
})
})*
$(pub fn $subarchive_resource(&self) -> &opt!(
$subarchive_type, $is_optional_subarchive)
{
&self.$subarchive_resource
})*
fn signature_name(archive_name: &str) -> String {
format!("{}.archive", archive_name)
}
}
impl ::std::fmt::Debug for $name {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f,
concat!(stringify!($name), " {{ ",
intersperse!(""
$(, concat!(stringify!($struct_resource), ": {:?}"))*
$(, concat!(stringify!($vector_resource), ": {:?}"))*
$(, concat!(stringify!($multivector_resource), ": {:?}"))*
$(, concat!(stringify!($raw_data_resource), ": {:?}"))*
$(, concat!(stringify!($subarchive_resource), ": {:?}"))*
),
" }}"),
$(self.$struct_resource(), )*
$(self.$vector_resource(), )*
$(self.$multivector_resource(), )*
$(self.$raw_data_resource, )*
$(self.$subarchive_resource, )*
)
}
}
impl $crate::Archive for $name {
const NAME: &'static str = stringify!($name);
const SCHEMA: &'static str = $archive_schema;
fn open(storage: ::std::rc::Rc<$crate::ResourceStorage>)
-> ::std::result::Result<Self, $crate::ResourceStorageError>
{
$(let $struct_resource;)*
$(let $vector_resource;)*
$(let $multivector_resource_index;
let $multivector_resource;)*
$(let $raw_data_resource;)*
{
storage.read(&Self::signature_name(Self::NAME), Self::SCHEMA)?;
$($struct_resource = check_resource!(
Self::read_resource(
&*storage,
stringify!($struct_resource),
$struct_schema
), $is_optional_struct);
)*
$($vector_resource = check_resource!(
Self::read_resource(
&*storage,
stringify!($vector_resource),
$element_schema), $is_optional_vector);
)*
$($multivector_resource_index = check_resource!(
Self::read_resource(
&*storage,
stringify!($multivector_resource_index),
&format!("index({})", $variadic_type_schema)),
$is_optional_multivector);
$multivector_resource = check_resource!(
Self::read_resource(
&*storage,
stringify!($multivector_resource),
$variadic_type_schema), $is_optional_multivector);
)*
$($raw_data_resource = check_resource!(
Self::read_resource(
&*storage,
stringify!($raw_data_resource),
$raw_data_schema), $is_optional_raw_data);
)*
}
$(
let $subarchive_resource = check_resource!(
$subarchive_type::open(
storage.subdir(&stringify!($subarchive_resource))),
$is_optional_subarchive
);)*
Ok(Self {
_storage: storage
$(,$struct_resource)*
$(,$vector_resource)*
$(,$multivector_resource: (
$multivector_resource_index,
$multivector_resource))*
$(,$raw_data_resource)*
$(,$subarchive_resource)*
})
}
}
#[derive(Clone)]
pub struct $builder_name {
storage: ::std::rc::Rc<$crate::ResourceStorage>
}
impl $builder_name {
$(pub fn $struct_setter(
&self,
resource: <$struct_type as $crate::Struct>::Item,
) -> ::std::io::Result<()> {
let data = unsafe {
::std::slice::from_raw_parts(resource.data, <$struct_type as $crate::Struct>::SIZE_IN_BYTES)
};
self.storage
.write(stringify!($struct_resource), $struct_schema, data)
})*
$(pub fn $vector_setter(
&self,
vector: &$crate::ArrayView<$element_type>,
) -> ::std::io::Result<()> {
self.storage
.write(stringify!($vector_resource), $element_schema, vector.as_ref())
}
pub fn $vector_start(
&self,
) -> ::std::io::Result<$crate::ExternalVector<$element_type>> {
$crate::create_external_vector(
&*self.storage,
stringify!($vector_resource),
$element_schema,
)
})*
$(pub fn $multivector_start(
&self,
) -> ::std::io::Result<
$crate::MultiVector<$index_type, $variadic_type>
> {
$crate::create_multi_vector(
&*self.storage,
stringify!($multivector_resource),
$variadic_type_schema,
)
})*
$(pub fn $raw_data_resource_setter(&self, data: &[u8]) -> ::std::io::Result<()> {
self.storage.write(
stringify!($raw_data_resource),
$raw_data_schema,
data,
)
})*
$(pub fn $subarchive_resource(
&self,
) -> Result<$subarchive_builder_type, $crate::ResourceStorageError> {
use $crate::ArchiveBuilder;
let storage = self.storage.subdir(stringify!($subarchive_resource));
$subarchive_builder_type::new(storage)
}
)*
}
impl $crate::ArchiveBuilder for $builder_name {
const NAME: &'static str = stringify!($name);
const SCHEMA: &'static str = $archive_schema;
fn new(
storage: ::std::rc::Rc<$crate::ResourceStorage>,
) -> Result<Self, $crate::ResourceStorageError> {
$crate::create_archive::<Self>(&storage)?;
Ok(Self { storage })
}
}
}
}
#[cfg(test)]
mod test {
use super::super::helper::Int;
use super::super::structbuf::StructBuf;
#[test]
#[allow(dead_code)]
fn test_debug() {
define_struct!(
A,
RefA,
RefMutA,
"no_schema",
4,
(x, set_x, u32, 0, 16),
(y, set_y, u32, 16, 16)
);
let a = StructBuf::<A>::new();
let output = format!("{:?}", a);
assert_eq!(output, "StructBuf { resource: A { x: 0, y: 0 } }");
}
macro_rules! define_enum_test {
($test_name:ident, $type:tt, $is_signed:expr, $val1:expr, $val2:expr) => {
#[test]
#[allow(dead_code)]
fn $test_name() {
#[derive(Debug, PartialEq, Eq)]
#[repr($type)]
pub enum Variant {
X = $val1,
Y = $val2,
}
impl Int for Variant {
const IS_SIGNED: bool = $is_signed;
}
define_struct!(
A,
RefA,
RefMutA,
"no_schema",
1,
(x, set_x, Variant: $type, 0, 2)
);
let mut a = StructBuf::<A>::new();
let output = format!("{:?}", a);
assert_eq!(output, "StructBuf { resource: A { x: X } }");
a.get_mut().set_x(Variant::Y);
let output = format!("{:?}", a);
assert_eq!(output, "StructBuf { resource: A { x: Y } }");
}
};
}
define_enum_test!(test_enum_u8_1, u8, false, 0, 1);
define_enum_test!(test_enum_u8_2, u8, false, 0, 2);
define_enum_test!(test_enum_u16_1, u16, false, 0, 1);
define_enum_test!(test_enum_u16_2, u16, false, 0, 2);
define_enum_test!(test_enum_u32_1, u32, false, 0, 1);
define_enum_test!(test_enum_u32_2, u32, false, 0, 2);
define_enum_test!(test_enum_u64_1, u64, false, 0, 1);
define_enum_test!(test_enum_u64_2, u64, false, 0, 2);
define_enum_test!(test_enum_i16_1, i16, true, 0, 1);
define_enum_test!(test_enum_i16_2, i16, true, 0, -1);
define_enum_test!(test_enum_i32_1, i32, true, 0, 1);
define_enum_test!(test_enum_i32_2, i32, true, 0, -1);
define_enum_test!(test_enum_i64_1, i64, true, 0, 1);
define_enum_test!(test_enum_i64_2, i64, true, 0, -1);
#[test]
#[allow(warnings)]
fn test_archive_compilation() {
use super::Ref;
define_struct!(
A,
RefA,
RefMutA,
"no_schema",
4,
(x, set_x, u32, 0, 16),
(y, set_y, u32, 16, 16)
);
define_struct!(
B,
RefB,
RefMutB,
"no_schema",
4,
(x, set_x, u32, 0, 16),
(y, set_y, u32, 16, 16)
);
define_index!(
IndexType32,
RefIndexType32,
RefMutIndexType32,
"IndexType32 schema",
4,
32
);
define_variadic_struct!(Ts, RefTs, BuilderTs, IndexType32,
0 => (A, add_a),
1 => (B, add_b));
define_archive!(SubArch, SubArchBuilder, "SubArch schema";
;
;
;
(raw, set_raw, "raw schema", false)
;
);
define_archive!(Arch, ArchBuilder, "Arch schema";
(a, set_a, A, "a schema", false),
(b, set_b, A, "b schema", true);
(v, set_v, start_v, A, "v schema", false),
(w, set_w, start_w, A, "w schema", true);
(mv, start_mv, Ts, "mv schema", mv_index, IndexType32, false),
(mw, start_mw, Ts, "mw schema", mw_index, IndexType32, true);
(r, set_r, "r schema", false),
(s, set_s, "s schema", true);
(arch, SubArch, SubArchBuilder, "arch schema", false),
(opt_arch, SubArch, SubArchBuilder, "opt_arch schema", true)
);
}
}