mod format;
mod manifest;
mod mmap;
mod paging;
pub use format::{BundleFormat, BundleReader, BundleWriter};
pub use manifest::{BundleManifest, ModelEntry, ModelMetadata};
pub use mmap::{MappedFile, MappedRegion, MemoryMappedFile};
pub use paging::{PagedBundle, PagingConfig, PagingStats};
use crate::error::Result;
use std::collections::HashMap;
use std::path::Path;
pub const BUNDLE_MAGIC: &[u8; 8] = b"APBUNDLE";
pub const BUNDLE_VERSION: u32 = 1;
pub const DEFAULT_PAGE_SIZE: usize = 4096;
pub const DEFAULT_MAX_MEMORY: usize = 100 * 1024 * 1024;
#[derive(Debug)]
pub struct ModelBundle {
path: Option<String>,
manifest: BundleManifest,
models: HashMap<String, Vec<u8>>,
config: BundleConfig,
}
#[derive(Debug, Clone)]
pub struct BundleConfig {
pub compress: bool,
pub max_memory: usize,
pub page_size: usize,
pub prefetch: bool,
}
impl Default for BundleConfig {
fn default() -> Self {
Self {
compress: false,
max_memory: DEFAULT_MAX_MEMORY,
page_size: DEFAULT_PAGE_SIZE,
prefetch: true,
}
}
}
impl BundleConfig {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn with_compression(mut self, compress: bool) -> Self {
self.compress = compress;
self
}
#[must_use]
pub fn with_max_memory(mut self, max_memory: usize) -> Self {
self.max_memory = max_memory.max(DEFAULT_PAGE_SIZE);
self
}
#[must_use]
pub fn with_page_size(mut self, page_size: usize) -> Self {
self.page_size = page_size.max(512);
self
}
#[must_use]
pub fn with_prefetch(mut self, prefetch: bool) -> Self {
self.prefetch = prefetch;
self
}
}
impl ModelBundle {
#[must_use]
pub fn new() -> Self {
Self {
path: None,
manifest: BundleManifest::new(),
models: HashMap::new(),
config: BundleConfig::default(),
}
}
#[must_use]
pub fn builder(path: impl Into<String>) -> BundleBuilder {
BundleBuilder::new(path)
}
pub fn load(path: impl AsRef<Path>) -> Result<Self> {
let path_str = path.as_ref().to_string_lossy().to_string();
let mut reader = BundleReader::open(path)?;
let manifest = reader.read_manifest()?;
let models = reader.read_all_models(&manifest)?;
Ok(Self {
path: Some(path_str),
manifest,
models,
config: BundleConfig::default(),
})
}
pub fn load_paged(path: impl AsRef<Path>, max_memory: usize) -> Result<PagedBundle> {
PagedBundle::open(path, PagingConfig::new().with_max_memory(max_memory))
}
pub fn save(&self, path: impl AsRef<Path>) -> Result<()> {
let writer = BundleWriter::create(path)?;
writer.write_bundle(&self.manifest, &self.models)
}
pub fn add_model(&mut self, name: impl Into<String>, data: Vec<u8>) -> &mut Self {
let name = name.into();
let size = data.len();
self.manifest.add_model(ModelEntry::new(&name, size));
self.models.insert(name, data);
self
}
#[must_use]
pub fn get_model(&self, name: &str) -> Option<&[u8]> {
self.models.get(name).map(Vec::as_slice)
}
#[must_use]
pub fn get_metadata(&self, name: &str) -> Option<&ModelEntry> {
self.manifest.get_model(name)
}
#[must_use]
pub fn model_names(&self) -> Vec<&str> {
self.manifest.model_names()
}
#[must_use]
pub fn len(&self) -> usize {
self.models.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.models.is_empty()
}
#[must_use]
pub fn total_size(&self) -> usize {
self.models.values().map(Vec::len).sum()
}
#[must_use]
pub fn manifest(&self) -> &BundleManifest {
&self.manifest
}
#[must_use]
pub fn config(&self) -> &BundleConfig {
&self.config
}
}
impl Default for ModelBundle {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct BundleBuilder {
path: String,
models: HashMap<String, Vec<u8>>,
metadata: HashMap<String, ModelMetadata>,
config: BundleConfig,
}
impl BundleBuilder {
#[must_use]
pub fn new(path: impl Into<String>) -> Self {
Self {
path: path.into(),
models: HashMap::new(),
metadata: HashMap::new(),
config: BundleConfig::default(),
}
}
#[must_use]
pub fn add_model(mut self, name: impl Into<String>, data: Vec<u8>) -> Self {
let name = name.into();
self.metadata
.insert(name.clone(), ModelMetadata::new(data.len()));
self.models.insert(name, data);
self
}
#[must_use]
pub fn add_model_with_metadata(
mut self,
name: impl Into<String>,
data: Vec<u8>,
metadata: ModelMetadata,
) -> Self {
let name = name.into();
self.metadata.insert(name.clone(), metadata);
self.models.insert(name, data);
self
}
#[must_use]
pub fn with_config(mut self, config: BundleConfig) -> Self {
self.config = config;
self
}
pub fn build(self) -> Result<ModelBundle> {
let mut bundle = ModelBundle::new();
bundle.config = self.config;
for (name, data) in self.models {
bundle.add_model(name, data);
}
bundle.save(&self.path)?;
bundle.path = Some(self.path);
Ok(bundle)
}
}
#[cfg(test)]
#[path = "bundle_tests.rs"]
mod tests;