use error::ResourceStorageError;
use storage::ResourceStorage;
use std::cell::RefCell;
use std::convert::From;
use std::fmt::Debug;
use std::rc::Rc;
pub trait Struct: Clone + Debug + PartialEq + From<*const u8> {
const SCHEMA: &'static str;
const SIZE_IN_BYTES: usize;
type Mut: StructMut + AsRef<Self>;
fn as_ptr(&self) -> *const u8;
}
pub trait StructMut: Debug + From<*mut u8> {
type Const: Struct;
fn as_mut_ptr(&mut self) -> *mut u8;
}
pub trait Index: Struct {
type IndexMut: IndexMut;
fn value(&self) -> usize;
}
pub trait IndexMut: StructMut {
fn set_value(&mut self, value: usize);
}
pub type TypeIndex = u8;
pub trait VariadicStruct: Clone + Debug + PartialEq + From<(TypeIndex, *const u8)> {
type ItemBuilder: From<*mut Vec<u8>>;
fn size_in_bytes(&self) -> usize;
}
pub trait Archive: Debug + Clone {
const NAME: &'static str;
const SCHEMA: &'static str;
fn open(storage: Rc<RefCell<ResourceStorage>>) -> Result<Self, ResourceStorageError>;
}
pub trait ArchiveBuilder: Clone {
const NAME: &'static str;
const SCHEMA: &'static str;
fn new(storage: Rc<RefCell<ResourceStorage>>) -> Result<Self, ResourceStorageError>;
}
#[macro_export]
macro_rules! define_struct {
($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!($name, $name_mut, $schema, $size_in_bytes
$(,($field, $field_setter, $type: $type, $offset, $bit_size))*
);
};
($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)]
pub struct $name {
data: *const u8,
}
impl $name {
$(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 ::std::fmt::Debug for $name {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f,
concat!(stringify!($name), " {{ ",
intersperse!($(concat!( stringify!($field), ": {:?}")), *), " }}"),
$(self.$field(),)*)
}
}
impl ::std::cmp::PartialEq for $name {
fn eq(&self, other: &$name) -> bool {
$(self.$field() == other.$field()) && *
}
}
impl ::std::convert::From<*const u8> for $name {
fn from(data: *const u8) -> Self {
Self { data }
}
}
impl $crate::Struct for $name {
const SCHEMA: &'static str = $schema;
const SIZE_IN_BYTES: usize = $size_in_bytes;
type Mut = $name_mut;
fn as_ptr(&self) -> *const u8 {
self.data
}
}
pub struct $name_mut {
data: *mut u8,
}
impl $name_mut {
$(pub fn $field(&self) -> $type {
let value = read_bytes!($primitive_type, self.data, $offset, $bit_size);
unsafe { ::std::mem::transmute::<$primitive_type, $type>(value) }
})*
$(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)
})*
pub fn fill_from(&mut self, other: &$name) {
$(self.$field_setter(other.$field());)*
}
}
impl ::std::fmt::Debug for $name_mut {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
self.as_ref().fmt(f)
}
}
impl ::std::convert::From<*mut u8> for $name_mut {
fn from(data: *mut u8) -> Self {
Self { data }
}
}
impl $crate::StructMut for $name_mut {
type Const = $name;
fn as_mut_ptr(&mut self) -> *mut u8 {
self.data
}
}
impl ::std::convert::AsRef<$name> for $name_mut {
fn as_ref(&self) -> &$name {
unsafe { &*(self as *const $name_mut as *const $name) }
}
}
};
}
#[macro_export]
macro_rules! define_index {
($name:ident, $name_mut:ident, $schema:expr, $size_in_bytes:expr, $size_in_bits:expr) => {
define_struct!(
$name,
$name_mut,
$schema,
$size_in_bytes,
(value, set_value, u64, 0, $size_in_bits)
);
impl $crate::Index for $name {
type IndexMut = $name_mut;
fn value(&self) -> usize {
self.value() as usize
}
}
impl $crate::IndexMut for $name_mut {
fn set_value(&mut self, value: usize) {
self.set_value(value as u64);
}
}
};
}
#[macro_export]
macro_rules! define_variadic_struct {
($name:ident, $item_builder_name:ident, $index_type:path,
$($type_index:expr => ($type:tt, $add_type_fn:ident)),+) =>
{
#[derive(Clone, PartialEq)]
pub enum $name {
$($type($type),)*
}
impl ::std::convert::From<($crate::TypeIndex, *const u8)> for $name {
fn from((type_index, data): ($crate::TypeIndex, *const u8)) -> Self {
match type_index {
$($type_index => $name::$type($type::from(data))),+,
_ => panic!(concat!(
"invalid type index {} for type ", stringify!($name)), type_index),
}
}
}
impl ::std::fmt::Debug for $name {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
match *self {
$($name::$type(ref inner) => write!(f, "{:?}", inner)),+
}
}
}
impl $crate::VariadicStruct for $name {
type ItemBuilder = $item_builder_name;
fn size_in_bytes(&self) -> usize {
match *self {
$($name::$type(_) => $type::SIZE_IN_BYTES),+
}
}
}
pub struct $item_builder_name {
data: *mut Vec<u8>
}
impl $item_builder_name {
$(pub fn $add_type_fn(&mut self) -> $crate::HandleMut<<$type as $crate::Struct>::Mut> {
let data = unsafe { &mut *self.data };
let old_len = data.len();
let increment = 1 + $type::SIZE_IN_BYTES;
data.resize(old_len + increment, 0);
data[old_len - $crate::PADDING_SIZE] = $type_index;
$crate::HandleMut::new(<$type as $crate::Struct>::Mut::from(
&mut data[1 + old_len - $crate::PADDING_SIZE] as *mut _
))
})*
}
impl<'a> ::std::convert::From<*mut Vec<u8>> for $item_builder_name {
fn from(data: *mut Vec<u8>) -> Self {
Self { 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 resources
$(($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<::std::cell::RefCell<$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: &mut $crate::ResourceStorage,
name: &str,
schema: &str,
) -> Result<R, $crate::ResourceStorageError>
where
R: From<$crate::MemoryDescriptor>,
{
storage.read(name, schema).map(R::from)
}
$(pub fn $struct_resource(&self) -> opt!(
$crate::Handle<$struct_type>, $is_optional_struct)
{
static_if!($is_optional_struct, {
self.$struct_resource.as_ref().map(|mem_desc| {
$crate::Handle::new($struct_type::from(mem_desc.data()))
})
}, {
$crate::Handle::new($struct_type::from(self.$struct_resource.data()))
})
})*
$(pub fn $vector_resource(&self) -> opt!(
$crate::ArrayView<$element_type>, $is_optional_vector)
{
static_if!($is_optional_vector, {
self.$vector_resource.as_ref().map($crate::ArrayView::new)
}, {
$crate::ArrayView::new(&self.$vector_resource)
})
})*
$(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($crate::ArrayView::new)
.and_then(move |index| {
res_mem_desc.map(move |mem_desc| {
$crate::MultiArrayView::new(index, mem_desc)
})
})
}, {
$crate::MultiArrayView::new(
$crate::ArrayView::new(&self.$multivector_resource.0),
&self.$multivector_resource.1,
)
})
})*
$(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 {
::std::slice::from_raw_parts(
mem_desc.data(),
mem_desc.size_in_bytes())
}
})
}, {
unsafe {
::std::slice::from_raw_parts(
self.$raw_data_resource.data(),
self.$raw_data_resource.size_in_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<::std::cell::RefCell<$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;)*
{
let res_storage = &mut *storage.borrow_mut();
res_storage.read(&Self::signature_name(Self::NAME), Self::SCHEMA)?;
$($struct_resource = check_resource!(
Self::read_resource(
res_storage,
stringify!($struct_resource),
$struct_schema
), $is_optional_struct);
)*
$($vector_resource = check_resource!(
Self::read_resource(
res_storage,
stringify!($vector_resource),
$element_schema), $is_optional_vector);
)*
$($multivector_resource_index = check_resource!(
Self::read_resource(
res_storage,
stringify!($multivector_resource_index),
&format!("index({})", $variadic_type_schema)),
$is_optional_multivector);
$multivector_resource = check_resource!(
Self::read_resource(
res_storage,
stringify!($multivector_resource),
$variadic_type_schema), $is_optional_multivector);
)*
$($raw_data_resource = check_resource!(
Self::read_resource(
res_storage,
stringify!($raw_data_resource),
$raw_data_schema), $is_optional_raw_data);
)*
}
$(
let $subarchive_resource = check_resource!(
$subarchive_type::open(
storage.borrow().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<::std::cell::RefCell<$crate::ResourceStorage>>
}
impl $builder_name {
$(pub fn $struct_setter(
&mut self,
resource: &<$struct_type as $crate::Struct>::Mut,
) -> ::std::io::Result<()> {
let data = unsafe {
::std::slice::from_raw_parts(resource.data, $struct_type::SIZE_IN_BYTES)
};
self.storage
.borrow_mut()
.write(stringify!($struct_resource), $struct_schema, data)
})*
$(pub fn $vector_setter(
&mut self,
vector: &$crate::ArrayView<$element_type>,
) -> ::std::io::Result<()> {
self.storage
.borrow_mut()
.write(stringify!($vector_resource), $element_schema, vector.as_ref())
}
pub fn $vector_start(
&mut self,
) -> ::std::io::Result<$crate::ExternalVector<$element_type>> {
$crate::create_external_vector(
&mut *self.storage.borrow_mut(),
stringify!($vector_resource),
$element_schema,
)
})*
$(pub fn $multivector_start(
&mut self,
) -> ::std::io::Result<
$crate::MultiVector<$index_type, $variadic_type>
> {
$crate::create_multi_vector(
&mut *self.storage.borrow_mut(),
stringify!($multivector_resource),
$variadic_type_schema,
)
})*
$(pub fn $raw_data_resource_setter(&mut self, data: &[u8]) -> ::std::io::Result<()> {
self.storage.borrow_mut().write(
stringify!($raw_data_resource),
$raw_data_schema,
data,
)
})*
$(pub fn $subarchive_resource(
&mut self,
) -> Result<$subarchive_builder_type, $crate::ResourceStorageError> {
use $crate::ArchiveBuilder;
let storage = self.storage.borrow().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<::std::cell::RefCell<$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,
AMut,
"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, AMut, "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.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::Struct;
define_struct!(
A,
AMut,
"no_schema",
4,
(x, set_x, u32, 0, 16),
(y, set_y, u32, 16, 16)
);
define_index!(IndexType32, IndexType32Mut, "IndexType32 schema", 4, 32);
define_variadic_struct!(Ts, TsBuilder, IndexType32,
0 => (A, add_a));
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)
);
}
}