horntail 0.4.2

maplestory resource file unpack lib
Documentation
use crate::Error;
use crate::extra::iter::ComponentIter;
use crate::extra::{Entry, EntryValue};
use std::cell::OnceCell;
use std::path::Path;

const HASH_FIXED_KEY: usize = 0x6d61706c65; // maple

#[inline(always)]
fn hash_builder() -> ahash::RandomState {
    ahash::RandomState::with_seed(HASH_FIXED_KEY)
}

struct SequencedCache {
    container: Vec<EntryCache>,
    indexes: ahash::HashMap<u64, Vec<usize>>,
}

pub struct EntryCache {
    entry: Entry,
    cache: OnceCell<SequencedCache>,
}

impl AsRef<Entry> for EntryCache {
    fn as_ref(&self) -> &Entry {
        &self.entry
    }
}

impl TryFrom<Entry> for EntryCache {
    type Error = Error;

    fn try_from(entry: Entry) -> Result<Self, Self::Error> {
        let cache = Self {
            entry,
            cache: OnceCell::new(),
        };
        cache.cache_map()?;
        Ok(cache)
    }
}

impl EntryCache {
    #[inline]
    fn cache_map(&self) -> Result<&SequencedCache, Error> {
        if let Some(cache) = self.cache.get() {
            return Ok(cache);
        }
        let builder = hash_builder();

        let mut container = vec![];
        let mut indexes: ahash::HashMap<u64, Vec<usize>> = ahash::HashMap::default();

        self.entry.try_iter()?.for_each(|e| {
            let index = container.len();
            let hash = builder.hash_one(e.name().as_bytes());
            container.push(EntryCache {
                entry: e,
                cache: OnceCell::new(),
            });
            match indexes.entry(hash) {
                std::collections::hash_map::Entry::Occupied(mut e) => e.get_mut().push(index),
                std::collections::hash_map::Entry::Vacant(e) => {
                    e.insert(vec![index]);
                }
            };
        });

        if self
            .cache
            .set(SequencedCache { container, indexes })
            .is_ok()
        {
            return Ok(self.cache.get().unwrap());
        }
        Err(Error::Unexpected(
            "init cache failed on `OnceCell::set`".into(),
        ))
    }

    #[inline]
    pub fn name(&self) -> &str {
        self.entry.name()
    }

    #[inline]
    pub fn value(&self) -> &EntryValue {
        self.entry.value()
    }

    #[inline]
    pub fn has_children(&self) -> bool {
        self.entry.has_children()
    }

    #[inline]
    pub fn try_get(&self, name: &str) -> Result<Option<&EntryCache>, Error> {
        let hash = hash_builder().hash_one(name.as_bytes());
        let sc = self.cache_map()?;
        let Some(indexes) = sc.indexes.get(&hash) else {
            return Ok(None);
        };
        if indexes.len() == 1 {
            return Ok(sc.container.get(indexes[0]));
        }
        for index in indexes.iter().copied() {
            let Some(x) = sc.container.get(index) else {
                return Ok(None);
            };
            if x.name().eq(name) {
                return Ok(Some(x));
            }
        }
        Ok(None)
    }

    #[inline]
    pub fn get_exact(&self, name: &str) -> &EntryCache {
        self.get(name).unwrap_or_else(|| panic!("entry not exists"))
    }

    #[inline]
    pub fn get(&self, name: &str) -> Option<&EntryCache> {
        self.try_get(name)
            .unwrap_or_else(|e| panic!("get entry failed: {e}"))
    }

    pub fn try_get_by_path<P: AsRef<Path>>(&self, path: P) -> Result<Option<&EntryCache>, Error> {
        let path = path.as_ref();
        let mut components = ComponentIter::from(path.components());
        let Some(first) = components.next() else {
            return Ok(None);
        };
        let mut cursor = self.try_get(first)?;
        for name in components {
            if let Some(entry) = cursor {
                cursor = entry.try_get(name)?;
            } else {
                return Ok(None);
            }
        }
        Ok(cursor)
    }

    #[inline]
    pub fn get_by_path<P: AsRef<Path>>(&self, path: P) -> Option<&EntryCache> {
        self.try_get_by_path(path)
            .unwrap_or_else(|e| panic!("get_by_path: {e}"))
    }

    #[inline]
    pub fn get_by_path_exact<P: AsRef<Path>>(&self, path: P) -> &EntryCache {
        self.get_by_path(path)
            .unwrap_or_else(|| panic!("path not exists"))
    }

    #[inline]
    pub fn try_to<'a, T: TryFrom<&'a EntryCache, Error = impl Into<Error>>>(
        &'a self,
    ) -> Result<T, Error> {
        T::try_from(self).map_err(|e| e.into())
    }

    #[inline]
    pub fn to<'a, T: TryFrom<&'a EntryCache, Error = impl Into<Error>>>(&'a self) -> T {
        self.try_to().unwrap_or_else(|e| panic!("to: {e}"))
    }

    #[inline]
    pub fn try_get_value<'a, T: TryFrom<&'a EntryCache, Error = impl Into<Error>>>(
        &'a self,
        name: &str,
    ) -> Result<Option<T>, Error> {
        if let Some(value) = self.try_get(name)? {
            Ok(Some(value.try_to::<T>()?))
        } else {
            Ok(None)
        }
    }

    #[inline]
    pub fn get_value<'a, T: TryFrom<&'a EntryCache, Error = impl Into<Error>>>(
        &'a self,
        name: &str,
    ) -> Option<T> {
        self.try_get_value::<T>(name)
            .unwrap_or_else(|e| panic!("get_value failed: {e}"))
    }

    #[inline]
    pub fn try_get_default<'a, T: TryFrom<&'a EntryCache, Error = impl Into<Error>> + Default>(
        &'a self,
        name: &str,
    ) -> Result<T, Error> {
        Ok(self
            .try_get_value::<T>(name)?
            .unwrap_or_else(|| T::default()))
    }

    #[inline]
    pub fn get_default<'a, T: TryFrom<&'a EntryCache, Error = impl Into<Error>> + Default>(
        &'a self,
        name: &str,
    ) -> T {
        self.try_get_default::<T>(name)
            .unwrap_or_else(|e| panic!("get_default: {e}"))
    }

    #[inline]
    pub fn try_get_or_else<'a, T: TryFrom<&'a EntryCache, Error = impl Into<Error>>>(
        &'a self,
        name: &str,
        f: impl FnOnce() -> Result<T, T::Error>,
    ) -> Result<T, Error> {
        if let Some(value) = self.try_get_value::<T>(name)? {
            Ok(value)
        } else {
            Ok(f().map_err(|e| e.into())?)
        }
    }

    #[inline]
    pub fn get_or_else<'a, T: TryFrom<&'a EntryCache, Error = impl Into<Error>>>(
        &'a self,
        name: &str,
        f: impl FnOnce() -> T,
    ) -> T {
        self.try_get_or_else::<T>(name, || Ok(f()))
            .unwrap_or_else(|e| panic!("get_or_else: {e}"))
    }

    #[inline]
    pub fn try_iter<'a>(&'a self) -> Result<Box<dyn Iterator<Item = &'a EntryCache> + 'a>, Error> {
        let m = self.cache_map()?;
        Ok(Box::new(m.container.iter()))
    }

    #[inline]
    pub fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = &'a EntryCache> + 'a> {
        self.try_iter().unwrap_or_else(|e| panic!("iter: {e}"))
    }
}