//! The standard object store which should fit all needs. use std::{cell::RefCell, ops::Deref}; use crate::Store; /// This effectively acts like a handle but exists to be usable from the actual `crate::Handle` implementation which adds caches on top. /// Each store is quickly cloned and contains thread-local state for shared packs. pub struct Handle where S: Deref + Clone, { pub(crate) store: S, /// Defines what happens when there is no more indices to load. pub refresh: RefreshMode, /// The maximum recursion depth for resolving ref-delta base objects, that is objects referring to other objects within /// a pack. /// Recursive loops are possible only in purposefully crafted packs. /// This value doesn't have to be huge as in typical scenarios, these kind of objects are rare and chains supposedly are /// even more rare. pub max_recursion_depth: usize, /// If true, replacements will not be performed even if these are available. pub ignore_replacements: bool, pub(crate) token: Option, snapshot: RefCell, packed_object_count: RefCell>, } /// Decide what happens when all indices are loaded. #[derive(Clone, Copy)] pub enum RefreshMode { /// Check for new or changed pack indices (and pack data files) when the last known index is loaded. /// During runtime we will keep pack indices stable by never reusing them, however, there is the option for /// clearing internal caches which is likely to change pack ids and it will trigger unloading of packs as they are missing on disk. AfterAllIndicesLoaded, /// Use this if you expect a lot of missing objects that shouldn't trigger refreshes even after all packs are loaded. /// This comes at the risk of not learning that the packs have changed in the mean time. Never, } impl Default for RefreshMode { fn default() -> Self { RefreshMode::AfterAllIndicesLoaded } } impl RefreshMode { /// Set this refresh mode to never refresh. pub fn never(&mut self) { *self = RefreshMode::Never; } } /// pub mod find; /// pub mod prefix; mod header; /// pub mod iter; /// pub mod write; /// pub mod init; pub(crate) mod types; pub use types::Metrics; pub(crate) mod handle; /// pub mod load_index; /// pub mod verify; mod load_one; mod metrics; mod access; /// pub mod structure { use std::path::PathBuf; use crate::{store::load_index, types::IndexAndPacks, Store}; /// A record of a structural element of an object database. #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))] pub enum Record { /// A loose object database. LooseObjectDatabase { /// The root of the object database. objects_directory: PathBuf, /// The amount of object files. num_objects: usize, }, /// A pack index file Index { /// The location of the index file, path: PathBuf, /// Whether or not the index is mapped into memory. state: IndexState, }, /// A multi-index file MultiIndex { /// The location of the multi-index file, path: PathBuf, /// Whether or not the index is mapped into memory. state: IndexState, }, /// An empty slot was encountered, this is possibly happening as the ODB changes during query with /// a file being removed. Empty, } #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))] /// Possible stats of pack indices. pub enum IndexState { /// The index is active in memory because a mapping exists. Loaded, /// The index couldn't be unloaded as it was still in use, but that can happen another time. Disposable, /// The index isn't loaded/memory mapped. Unloaded, } impl Store { /// Return information about all files known to us as well as their loading state. /// /// Note that this call is expensive as it gathers additional information about loose object databases. /// Note that it may change as we collect information due to the highly volatile nature of the /// implementation. The likelihood of actual changes is low though as these still depend on something /// changing on disk and somebody reading at the same time. pub fn structure(&self) -> Result, load_index::Error> { let index = self.index.load(); if !index.is_initialized() { self.consolidate_with_disk_state(true, false /*load one new index*/)?; } let index = self.index.load(); let mut res: Vec<_> = index .loose_dbs .iter() .map(|db| Record::LooseObjectDatabase { objects_directory: db.path.clone(), num_objects: db.iter().count(), }) .collect(); for slot in index.slot_indices.iter().map(|idx| &self.files[*idx]) { let files = slot.files.load(); let record = match &**files { Some(index) => { let state = if index.is_disposable() { IndexState::Disposable } else if index.index_is_loaded() { IndexState::Loaded } else { IndexState::Unloaded }; match index { IndexAndPacks::Index(b) => Record::Index { path: b.index.path().into(), state, }, IndexAndPacks::MultiIndex(b) => Record::MultiIndex { path: b.multi_index.path().into(), state, }, } } None => Record::Empty, }; res.push(record); } Ok(res) } } }