#![forbid(unsafe_code)]
use crate::core::extent::ChunkId;
use crate::format::codec::{CodecError, Reader, Writer};
use crate::store::StoreError;
use crate::store::directory::DirEntry;
use crate::store::inode::Inode;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MutationOp {
Create {
parent: u64,
name: Vec<u8>,
ino: u64,
d_type: u8,
inode_id: ChunkId,
parent_inode_id: ChunkId,
},
Setattr {
ino: u64,
inode_id: ChunkId,
},
Unlink {
parent: u64,
name: Vec<u8>,
child: u64,
is_dir: bool,
parent_inode_id: ChunkId,
child_inode_id: Option<ChunkId>,
},
Rename {
src_parent: u64,
src_name: Vec<u8>,
dst_parent: u64,
dst_name: Vec<u8>,
src_ino: u64,
dst_ino: Option<u64>,
src_is_dir: bool,
sp_inode_id: ChunkId,
dp_inode_id: ChunkId,
src_child_inode_id: Option<ChunkId>,
dst_child_inode_id: Option<ChunkId>,
},
Write {
ino: u64,
size: u64,
chunks: Vec<(u64, ChunkId, Vec<u8>)>,
inode_id: ChunkId,
},
}
const OP_CREATE: u8 = 0x01;
const OP_SETATTR: u8 = 0x02;
const OP_UNLINK: u8 = 0x03;
const OP_RENAME: u8 = 0x04;
const OP_WRITE: u8 = 0x05;
impl MutationOp {
pub fn encode(&self) -> Vec<u8> {
let mut w = Writer::new();
match self {
MutationOp::Create {
parent,
name,
ino,
d_type,
inode_id,
parent_inode_id,
} => {
w.u8(OP_CREATE);
w.u64(*parent);
w.bytes16(name).expect("name fits u16");
w.u64(*ino);
w.u8(*d_type);
w.bytes(inode_id.as_bytes());
w.bytes(parent_inode_id.as_bytes());
}
MutationOp::Setattr { ino, inode_id } => {
w.u8(OP_SETATTR);
w.u64(*ino);
w.bytes(inode_id.as_bytes());
}
MutationOp::Unlink {
parent,
name,
child,
is_dir,
parent_inode_id,
child_inode_id,
} => {
w.u8(OP_UNLINK);
w.u64(*parent);
w.bytes16(name).expect("name fits u16");
w.u64(*child);
w.u8(*is_dir as u8);
w.bytes(parent_inode_id.as_bytes());
match child_inode_id {
Some(id) => {
w.u8(1);
w.bytes(id.as_bytes());
}
None => w.u8(0),
}
}
MutationOp::Rename {
src_parent,
src_name,
dst_parent,
dst_name,
src_ino,
dst_ino,
src_is_dir,
sp_inode_id,
dp_inode_id,
src_child_inode_id,
dst_child_inode_id,
} => {
w.u8(OP_RENAME);
w.u64(*src_parent);
w.bytes16(src_name).expect("name fits u16");
w.u64(*dst_parent);
w.bytes16(dst_name).expect("name fits u16");
w.u64(*src_ino);
match dst_ino {
Some(i) => {
w.u8(1);
w.u64(*i);
}
None => w.u8(0),
}
w.u8(*src_is_dir as u8);
w.bytes(sp_inode_id.as_bytes());
w.bytes(dp_inode_id.as_bytes());
match src_child_inode_id {
Some(id) => {
w.u8(1);
w.bytes(id.as_bytes());
}
None => w.u8(0),
}
match dst_child_inode_id {
Some(id) => {
w.u8(1);
w.bytes(id.as_bytes());
}
None => w.u8(0),
}
}
MutationOp::Write {
ino,
size,
chunks,
inode_id,
} => {
w.u8(OP_WRITE);
w.u64(*ino);
w.u64(*size);
w.u32(chunks.len() as u32);
for (off, cid, desc) in chunks {
w.u64(*off);
w.bytes(cid.as_bytes());
w.bytes32(desc).expect("descriptor fits u32");
}
w.bytes(inode_id.as_bytes());
}
}
w.into_bytes()
}
pub fn decode(bytes: &[u8]) -> Result<Self, CodecError> {
let mut r = Reader::new(bytes);
let op = r.u8()?;
Ok(match op {
OP_CREATE => {
let parent = r.u64()?;
let name = r.bytes16()?.to_vec();
let ino = r.u64()?;
let d_type = r.u8()?;
let inode_id = read_id(&mut r)?;
let parent_inode_id = read_id(&mut r)?;
MutationOp::Create {
parent,
name,
ino,
d_type,
inode_id,
parent_inode_id,
}
}
OP_SETATTR => {
let ino = r.u64()?;
let inode_id = read_id(&mut r)?;
MutationOp::Setattr { ino, inode_id }
}
OP_UNLINK => {
let parent = r.u64()?;
let name = r.bytes16()?.to_vec();
let child = r.u64()?;
let is_dir = r.u8()? != 0;
let parent_inode_id = read_id(&mut r)?;
let child_inode_id = if r.u8()? != 0 {
Some(read_id(&mut r)?)
} else {
None
};
MutationOp::Unlink {
parent,
name,
child,
is_dir,
parent_inode_id,
child_inode_id,
}
}
OP_RENAME => {
let src_parent = r.u64()?;
let src_name = r.bytes16()?.to_vec();
let dst_parent = r.u64()?;
let dst_name = r.bytes16()?.to_vec();
let src_ino = r.u64()?;
let dst_ino = if r.u8()? != 0 { Some(r.u64()?) } else { None };
let src_is_dir = r.u8()? != 0;
let sp_inode_id = read_id(&mut r)?;
let dp_inode_id = read_id(&mut r)?;
let src_child_inode_id = if r.u8()? != 0 {
Some(read_id(&mut r)?)
} else {
None
};
let dst_child_inode_id = if r.u8()? != 0 {
Some(read_id(&mut r)?)
} else {
None
};
MutationOp::Rename {
src_parent,
src_name,
dst_parent,
dst_name,
src_ino,
dst_ino,
src_is_dir,
sp_inode_id,
dp_inode_id,
src_child_inode_id,
dst_child_inode_id,
}
}
OP_WRITE => {
let ino = r.u64()?;
let size = r.u64()?;
let n = r.u32()? as usize;
if n > 1 << 20 {
return Err(CodecError::Malformed);
}
let mut chunks = Vec::with_capacity(n);
for _ in 0..n {
let off = r.u64()?;
let cid = read_id(&mut r)?;
let desc = r.bytes32()?.to_vec();
chunks.push((off, cid, desc));
}
let inode_id = read_id(&mut r)?;
MutationOp::Write {
ino,
size,
chunks,
inode_id,
}
}
_ => return Err(CodecError::Malformed),
})
}
}
fn read_id(r: &mut Reader<'_>) -> Result<ChunkId, CodecError> {
Ok(ChunkId::new(r.take(32)?.try_into().unwrap()))
}
pub struct EpochContext<'a> {
store: &'a crate::store::Store,
ep: &'a Epoch,
}
impl<'a> EpochContext<'a> {
pub fn new(store: &'a crate::store::Store, ep: &'a Epoch) -> Self {
Self { store, ep }
}
}
impl crate::core::materialize::DecoderContext for EpochContext<'_> {
fn fetch_object(
&self,
id: &ChunkId,
) -> Result<Vec<u8>, crate::core::materialize::MaterializeError> {
self.store.fetch_object_impl(id)
}
fn fetch_descriptor(
&self,
id: &ChunkId,
) -> Result<
crate::core::representation::Representation,
crate::core::materialize::MaterializeError,
> {
if let Some(bytes) = self.ep.overlay_chunk(id) {
let limits = *self.store.limits();
return crate::format::descriptor::decode(
&bytes,
limits.max_descriptor_bytes,
limits.max_inline_bytes,
limits.max_palette,
limits.max_period,
limits.max_chunk_size,
)
.map_err(|e| {
crate::core::materialize::MaterializeError::InvalidDescriptor(e.to_string())
});
}
self.store.fetch_descriptor(id)
}
fn decode_rans(
&self,
model: &[u8],
encoded: &[u8],
scale_bits: u8,
codec: crate::core::representation::RansCodec,
out_len: u64,
) -> Result<Vec<u8>, crate::core::materialize::MaterializeError> {
self.store
.decode_rans(model, encoded, scale_bits, codec, out_len)
}
fn universe_bytes(
&self,
universe: crate::core::representation::UniverseId,
seed: [u8; 16],
coordinate: u64,
range: std::ops::Range<u64>,
) -> Result<Vec<u8>, crate::core::materialize::MaterializeError> {
self.store.universe_bytes(universe, seed, coordinate, range)
}
}
#[derive(Debug, Default)]
pub struct Epoch {
pub seq: u64,
pub pending_inodes: std::collections::BTreeMap<u64, Inode>,
pub removed_inodes: std::collections::BTreeSet<u64>,
pub pending_entries: std::collections::BTreeMap<(u64, Vec<u8>), DirEntry>,
pub removed_entries: std::collections::BTreeSet<(u64, Vec<u8>)>,
pub pending_extents: std::collections::BTreeMap<(u64, u64), Vec<u8>>,
pub pending_chunks: std::collections::BTreeMap<ChunkId, Vec<u8>>,
pub staged_objects: std::collections::HashSet<ChunkId>,
pub feature_persisted: bool,
pub max_ino: u64,
}
impl Epoch {
pub fn is_empty(&self) -> bool {
self.seq == 0
&& self.pending_inodes.is_empty()
&& self.removed_inodes.is_empty()
&& self.pending_entries.is_empty()
&& self.removed_entries.is_empty()
&& self.pending_extents.is_empty()
&& self.pending_chunks.is_empty()
}
pub fn mark_staged(&mut self, id: ChunkId) {
self.staged_objects.insert(id);
}
pub fn is_staged(&self, id: &ChunkId) -> bool {
self.staged_objects.contains(id)
}
pub fn overlay_inode(&self, ino: u64, committed: Option<Inode>) -> Option<Inode> {
if self.removed_inodes.contains(&ino) {
return None;
}
if let Some(i) = self.pending_inodes.get(&ino) {
return Some(i.clone());
}
committed
}
pub fn overlay_entry(&self, parent: u64, name: &[u8]) -> Option<DirEntry> {
if self.removed_entries.contains(&(parent, name.to_vec())) {
return None;
}
if let Some(e) = self.pending_entries.get(&(parent, name.to_vec())) {
return Some(*e);
}
None
}
pub fn overlay_chunk(&self, cid: &ChunkId) -> Option<Vec<u8>> {
self.pending_chunks.get(cid).cloned()
}
pub fn overlay_extent(&self, ino: u64, offset: u64) -> Option<Vec<u8>> {
self.pending_extents.get(&(ino, offset)).cloned()
}
pub fn envelope(&mut self, op: &MutationOp) -> Vec<u8> {
self.seq += 1;
let mut w = Writer::new();
w.u64(self.seq);
w.bytes(&op.encode());
w.into_bytes()
}
pub fn envelope_seq(bytes: &[u8]) -> Result<u64, StoreError> {
let mut r = Reader::new(bytes);
let seq = r.u64().map_err(|e| StoreError::Descriptor(e.to_string()))?;
Ok(seq)
}
pub fn decode_envelope(bytes: &[u8]) -> Result<(u64, MutationOp), StoreError> {
let mut r = Reader::new(bytes);
let seq = r.u64().map_err(|e| StoreError::Descriptor(e.to_string()))?;
let op = MutationOp::decode(&bytes[r.pos()..])
.map_err(|e| StoreError::Descriptor(e.to_string()))?;
Ok((seq, op))
}
}