use std::sync::atomic::AtomicBool;
use crate::{ObjectId, Objects, ReadLimits};
mod encode;
mod graph;
mod midx;
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct WriteOptions {
pub commits: Vec<ObjectId>,
pub max_commits: usize,
}
impl Default for WriteOptions {
fn default() -> Self {
Self {
commits: Vec::new(),
max_commits: 64,
}
}
}
#[derive(Clone, Copy, Debug)]
#[non_exhaustive]
pub struct WriteLimits {
pub max_objects: usize,
pub max_packs: usize,
pub max_edges: usize,
pub max_input_bytes: u64,
pub max_visits: usize,
pub max_bytes: usize,
pub read: ReadLimits,
}
impl Default for WriteLimits {
fn default() -> Self {
Self {
max_objects: 5_000_000,
max_packs: 4096,
max_edges: 20_000_000,
max_input_bytes: 1024 * 1024 * 1024,
max_visits: 100_000_000,
max_bytes: 64 * 1024 * 1024,
read: ReadLimits::default(),
}
}
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct Written {
pub anchor: ObjectId,
pub objects: usize,
pub commits: usize,
pub bitmap: Vec<u8>,
pub reverse_index: Vec<u8>,
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct MultiPackWritten {
pub index: Vec<u8>,
pub bitmap: Written,
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum WriteError {
#[error("bitmap pack {0} is unavailable")]
Pack(ObjectId),
#[error("bitmap selection {0} is not an inventory commit")]
Commit(ObjectId),
#[error("bitmap inventory does not contain reachable object {0}")]
Closure(ObjectId),
#[error("bitmap writing limit exceeded: {0}")]
Limit(&'static str),
#[error("bitmap writing requires an unshallow object graph")]
Shallow,
#[error("validating bitmap graph")]
Graph(#[from] super::Error),
#[error("reading bitmap source")]
Read(#[from] crate::ObjectReadError),
#[error("encoding bitmap multi-pack index")]
MultiPack(#[from] crate::multi_pack_index::WriteError),
#[error("validating bitmap multi-pack index")]
Index(#[from] crate::multi_pack_index::Error),
#[error("bitmap writing cancelled")]
Cancelled,
}
impl Objects {
pub fn encode_pack_bitmap(
&self,
pack: ObjectId,
options: WriteOptions,
limits: WriteLimits,
cancel: &AtomicBool,
) -> Result<Written, WriteError> {
check(cancel)?;
if !self.shallow_roots().is_empty() {
return Err(WriteError::Shallow);
}
let packs = self.primary_packs_with_paths();
let (_, pack) = packs
.iter()
.find(|(_, source)| source.checksum() == pack)
.ok_or(WriteError::Pack(pack))?;
let count = pack.object_count();
bound_objects(count, limits)?;
pack.verify_index()?;
pack.check_unchanged()?;
let ids: Vec<_> = (0..count)
.map(|position| pack.identity_at(position))
.collect();
let order: Vec<_> = pack.positions_by_offset()?.collect();
let graph = graph::Graph::read(self.object_format(), &ids, limits, cancel, |position| {
pack.read(position, limits.read, cancel)
})?;
let written = encode::artifacts(
pack.checksum(),
&order,
&graph,
options,
limits,
false,
cancel,
)?;
pack.check_unchanged()?;
Ok(written)
}
pub fn encode_multi_pack_bitmap(
&self,
midx: crate::multi_pack_index::WriteOptions,
options: WriteOptions,
limits: WriteLimits,
cancel: &AtomicBool,
) -> Result<MultiPackWritten, WriteError> {
midx::encode(self, midx, options, limits, cancel)
}
}
fn bound_objects(count: usize, limits: WriteLimits) -> Result<(), WriteError> {
if count > limits.max_objects || count > u32::MAX as usize {
return Err(WriteError::Limit("objects"));
}
Ok(())
}
pub(super) fn check(cancel: &AtomicBool) -> Result<(), WriteError> {
if super::check(cancel).is_err() {
Err(WriteError::Cancelled)
} else {
Ok(())
}
}