119 lines · 4.9 KB · Rust
RawDownload
use std::{iter::FromIterator, path::PathBuf, sync::Arc}; use arc_swap::ArcSwap; use crate::{    store::types::{MutableIndexAndPack, SlotMapIndex},    Store,}; /// Options for use in [`Store::at_opts()`].#[derive(Copy, Clone, Debug)]pub struct Options {    /// How to obtain a size for the slot map.    pub slots: Slots,    /// The kind of hash we expect in our packs and would use for loose object iteration and object writing.    pub object_hash: git_hash::Kind,    /// If false, no multi-pack indices will be used. If true, they will be used if their hash matches `object_hash`.    pub use_multi_pack_index: bool,} impl Default for Options {    fn default() -> Self {        Options {            slots: Default::default(),            object_hash: Default::default(),            use_multi_pack_index: true,        }    }} /// Configures the amount of slots in the index slotmap, which is fixed throughout the existence of the store.#[derive(Copy, Clone, Debug)]pub enum Slots {    /// The amount of slots to use, that is the total amount of indices we can hold at a time.    /// Using this has the advantage of avoiding an initial directory listing of the repository, and is recommended    /// on the server side where the repository setup is controlled.    ///    /// Note that this won't affect their packs, as each index can have one or more packs associated with it.    Given(u16),    /// Compute the amount of slots needed, as probably best used on the client side where a variety of repositories is encountered.    AsNeededByDiskState {        /// 1.0 means no safety, 1.1 means 10% more slots than needed        multiplier: f32,        /// The minimum amount of slots to assume        minimum: usize,    },} impl Default for Slots {    fn default() -> Self {        Slots::AsNeededByDiskState {            multiplier: 1.1,            minimum: 32,        }    }} impl Store {    /// Open the store at `objects_dir` (containing loose objects and `packs/`), which must only be a directory for    /// the store to be created without any additional work being done.    /// `slots` defines how many multi-pack-indices as well as indices we can know about at a time, which includes    /// the allowance for all additional object databases coming in via `alternates` as well.    /// Note that the `slots` isn't used for packs, these are included with their multi-index or index respectively.    /// For example, In a repository with 250m objects and geometric packing one would expect 27 index/pack pairs,    /// or a single multi-pack index.    /// `replacements` is an iterator over pairs of old and new object ids for replacement support.    /// This means that when asking for object `X`, one will receive object `X-replaced` given an iterator like `Some((X, X-replaced))`.    pub fn at_opts(        objects_dir: impl Into<PathBuf>,        replacements: impl IntoIterator<Item = (git_hash::ObjectId, git_hash::ObjectId)>,        Options {            slots,            object_hash,            use_multi_pack_index,        }: Options,    ) -> std::io::Result<Self> {        let objects_dir = objects_dir.into();        if !objects_dir.is_dir() {            return Err(std::io::Error::new(                std::io::ErrorKind::Other, // TODO: use NotADirectory when stabilized                format!("'{}' wasn't a directory", objects_dir.display()),            ));        }        let slot_count = match slots {            Slots::Given(n) => n as usize,            Slots::AsNeededByDiskState { multiplier, minimum } => {                let mut db_paths = crate::alternate::resolve(&objects_dir)                    .map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))?;                db_paths.insert(0, objects_dir.clone());                let num_slots = super::Store::collect_indices_and_mtime_sorted_by_size(db_paths, None, None)                    .map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))?                    .len();                 ((num_slots as f32 * multiplier) as usize).max(minimum)            }        };        if slot_count > crate::store::types::PackId::max_indices() {            return Err(std::io::Error::new(                std::io::ErrorKind::Other,                "Cannot use more than 1^15 slots",            ));        }        let mut replacements: Vec<_> = replacements.into_iter().collect();        replacements.sort_by(|a, b| a.0.cmp(&b.0));         Ok(Store {            write: Default::default(),            replacements,            path: objects_dir,            files: Vec::from_iter(std::iter::repeat_with(MutableIndexAndPack::default).take(slot_count)),            index: ArcSwap::new(Arc::new(SlotMapIndex::default())),            use_multi_pack_index,            object_hash,            num_handles_stable: Default::default(),            num_handles_unstable: Default::default(),            num_disk_state_consolidation: Default::default(),        })    }}