use std::collections::HashMap;
use std::ptr::NonNull;
use std::fmt::Debug;
use once_cell::sync::OnceCell;
use bit_vec::BitVec;
use crate::tag::{TagType, TagTypeKey};
use crate::util::OpaquePtr;
mod state;
mod property;
mod util;
pub use state::*;
pub use property::*;
pub use util::*;
#[derive(Debug)]
pub struct Block {
name: &'static str,
spec: BlockSpec,
states: OnceCell<BlockStorage>,
}
pub type BlockKey = OpaquePtr<Block>;
#[derive(Debug)]
enum BlockStorage {
Single(BlockState),
Complex {
states: Vec<BlockState>,
properties: HashMap<&'static str, SharedProperty>,
default_state_index: usize
}
}
#[derive(Debug)]
pub enum BlockSpec {
Single,
Complex(&'static [&'static dyn UntypedProperty]),
}
impl Block {
pub const fn new(name: &'static str, spec: BlockSpec) -> Self {
Self {
name,
spec,
states: OnceCell::new()
}
}
#[inline]
pub fn get_name(&self) -> &'static str {
self.name
}
#[inline]
pub fn get_key(&'static self) -> BlockKey {
OpaquePtr::new(self)
}
fn get_storage(&'static self) -> &'static BlockStorage {
self.states.get_or_init(|| self.make_storage())
}
fn make_storage(&'static self) -> BlockStorage {
fn new_storage(properties: &'static [&'static dyn UntypedProperty]) -> BlockStorage {
if properties.is_empty() {
BlockStorage::Single(BlockState::build_singleton())
} else {
let (
properties,
states
) = BlockState::build_complex(properties);
BlockStorage::Complex {
states,
properties,
default_state_index: 0
}
}
}
let mut storage = match self.spec {
BlockSpec::Single => BlockStorage::Single(BlockState::build_singleton()),
BlockSpec::Complex(properties) => new_storage(properties),
};
let block_ptr = NonNull::from(self);
match &mut storage {
BlockStorage::Single( state) => {
state.set_block(block_ptr);
},
BlockStorage::Complex {
states,
..
} => {
for state in states {
state.set_block(block_ptr);
}
}
}
storage
}
#[inline]
pub fn get_default_state(&'static self) -> &'static BlockState {
self.get_storage().get_default_state()
}
#[inline]
pub fn get_states(&'static self) -> &'static [BlockState] {
self.get_storage().get_states()
}
}
impl PartialEq for &'static Block {
fn eq(&self, other: &Self) -> bool {
std::ptr::eq(*self, *other)
}
}
impl Eq for &'static Block {}
impl BlockStorage {
pub fn get_default_state(&self) -> &BlockState {
match self {
BlockStorage::Single(state) => state,
BlockStorage::Complex {
states,
default_state_index, ..
} => &states[*default_state_index]
}
}
pub fn get_states(&self) -> &[BlockState] {
match self {
BlockStorage::Single(state) => std::slice::from_ref(state),
BlockStorage::Complex { states, .. } => &states[..]
}
}
fn get_shared_prop(&self, name: &str) -> Option<&SharedProperty> {
match self {
BlockStorage::Single(_) => None,
BlockStorage::Complex {
properties, ..
} => properties.get(name)
}
}
fn get_shared_props(&self) -> Option<&HashMap<&'static str, SharedProperty>> {
match self {
BlockStorage::Single(_) => None,
BlockStorage::Complex {
properties, ..
} => Some(properties)
}
}
fn get_state_unchecked(&self, index: usize) -> &BlockState {
match self {
BlockStorage::Single(state) => {
debug_assert!(index == 0, "index != 0 with BlockStorage::Single");
state
},
BlockStorage::Complex { states, .. } => &states[index]
}
}
}
pub struct GlobalBlocks {
next_sid: u32,
block_to_indices: HashMap<BlockKey, (usize, u32)>,
ordered_states: Vec<&'static BlockState>,
name_to_blocks: HashMap<&'static str, &'static Block>,
tag_stores: HashMap<TagTypeKey, TagStore>
}
impl GlobalBlocks {
pub fn new() -> Self {
Self {
next_sid: 0,
block_to_indices: HashMap::new(),
ordered_states: Vec::new(),
name_to_blocks: HashMap::new(),
tag_stores: HashMap::new()
}
}
pub fn with_all(slice: &[&'static Block]) -> Result<Self, ()> {
let mut blocks = Self::new();
blocks.register_all(slice)?;
Ok(blocks)
}
pub fn register(&mut self, block: &'static Block) -> Result<(), ()> {
let states = block.get_states();
let states_count = states.len();
let sid = self.next_sid;
let idx = self.block_to_indices.len();
let next_sid = sid.checked_add(states_count as u32).ok_or(())?;
for store in self.tag_stores.values_mut() {
if let TagStore::Big(store) = store {
store.push(false);
}
}
if self.block_to_indices.insert(block.get_key(), (idx, sid)).is_none() {
self.next_sid = next_sid;
self.name_to_blocks.insert(block.name, block);
self.ordered_states.reserve(states_count);
for state in states {
self.ordered_states.push(state);
}
}
Ok(())
}
pub fn register_all(&mut self, slice: &[&'static Block]) -> Result<(), ()> {
let count = slice.len();
self.block_to_indices.reserve(count);
self.name_to_blocks.reserve(count);
for store in self.tag_stores.values_mut() {
if let TagStore::Big(store) = store {
store.reserve(count);
}
}
for &block in slice {
self.register(block)?;
}
Ok(())
}
pub fn get_sid_from(&self, state: &'static BlockState) -> Option<u32> {
let (_, block_offset) = *self.block_to_indices.get(&state.get_block().get_key())?;
Some(block_offset + state.get_index() as u32)
}
pub fn get_state_from(&self, sid: u32) -> Option<&'static BlockState> {
self.ordered_states.get(sid as usize).copied()
}
pub fn get_block_from_name(&self, name: &str) -> Option<&'static Block> {
self.name_to_blocks.get(name).cloned()
}
pub fn has_block(&self, block: &'static Block) -> bool {
self.block_to_indices.contains_key(&block.get_key())
}
pub fn has_state(&self, state: &'static BlockState) -> bool {
self.has_block(state.get_block())
}
pub fn check_state<E>(&self, state: &'static BlockState, err: impl FnOnce() -> E) -> Result<&'static BlockState, E> {
if self.has_state(state) { Ok(state) } else { Err(err()) }
}
pub fn register_tag_type(&mut self, tag_type: &'static TagType) {
self.tag_stores.insert(tag_type.get_key(), TagStore::Small(Vec::new()));
}
pub fn set_blocks_tag<I>(&mut self, tag_type: &'static TagType, enabled: bool, blocks: I) -> Result<(), ()>
where
I: IntoIterator<Item = &'static Block>
{
const MAX_SMALL_LEN: usize = 8;
let store = self.tag_stores.get_mut(&tag_type.get_key()).ok_or(())?;
for block in blocks {
if let TagStore::Small(vec) = store {
let idx = vec.iter().position(move |&b| b == block);
if enabled {
if idx.is_none() {
if vec.len() >= MAX_SMALL_LEN {
let mut new_vec = BitVec::from_elem(self.block_to_indices.len(), false);
for old_block in vec {
let (idx, _) = *self.block_to_indices.get(&old_block.get_key()).ok_or(())?;
new_vec.set(idx, true);
}
*store = TagStore::Big(new_vec);
} else {
vec.push(block);
}
}
} else if let Some(idx) = idx {
vec.swap_remove(idx);
}
}
if let TagStore::Big(vec) = store {
let (idx, _) = *self.block_to_indices.get(&block.get_key()).ok_or(())?;
vec.set(idx, enabled);
}
}
Ok(())
}
pub fn has_block_tag(&self, block: &'static Block, tag_type: &'static TagType) -> bool {
match self.tag_stores.get(&tag_type.get_key()) {
None => false,
Some(store) => {
match store {
TagStore::Small(vec) => vec.iter().any(move |&b| b == block),
TagStore::Big(vec) => match self.block_to_indices.get(&block.get_key()) {
None => false,
Some(&(idx, _)) => vec.get(idx).unwrap()
}
}
}
}
}
pub fn blocks_count(&self) -> usize {
self.block_to_indices.len()
}
pub fn states_count(&self) -> usize {
self.ordered_states.len()
}
pub fn tags_count(&self) -> usize {
self.tag_stores.len()
}
}
#[derive(Debug)]
enum TagStore {
Small(Vec<&'static Block>),
Big(BitVec)
}
#[macro_export]
macro_rules! blocks_specs {
($($v:vis $id:ident: [$($prop_const:ident),+];)*) => {
$(
$v static $id: [&'static dyn $crate::block::UntypedProperty; $crate::count!($($prop_const)+)] = [
$(&$prop_const),+
];
)*
};
}
#[macro_export]
macro_rules! blocks {
($global_vis:vis $static_id:ident $namespace:literal [
$($block_id:ident $block_name:literal $($spec_id:ident)?),*
$(,)?
]) => {
$($global_vis static $block_id: $crate::block::Block = $crate::block::Block::new(
concat!($namespace, ':', $block_name),
$crate::_blocks_spec!($($spec_id)?)
);)*
$global_vis static $static_id: [&'static $crate::block::Block; $crate::count!($($block_id)*)] = [
$(&$block_id),*
];
};
}
#[macro_export]
macro_rules! _blocks_spec {
() => { $crate::block::BlockSpec::Single };
($spec_id:ident) => { $crate::block::BlockSpec::Complex(&$spec_id) }
}