use crate::change::*;
use crate::small_string::*;
use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::io::Write;
use std::rc::Rc;
mod change_id;
pub use change_id::*;
mod vertex;
pub use vertex::*;
mod edge;
pub use edge::*;
mod hash;
pub use hash::*;
mod inode;
pub use inode::*;
mod inode_metadata;
pub use inode_metadata::*;
mod path_id;
pub use path_id::*;
mod merkle;
pub use merkle::*;
#[cfg(feature = "dump")]
pub mod channel_dump;
mod block;
pub(crate) use block::*;
pub trait Base32: Sized {
fn to_base32(&self) -> String;
fn from_base32(b: &[u8]) -> Option<Self>;
}
pub mod sanakirja;
pub type ApplyTimestamp = u64;
pub struct Channel<T: TxnT> {
pub graph: T::Graph,
pub changes: T::Changeset,
pub revchanges: T::Revchangeset,
pub states: T::Channelstates,
pub apply_counter: ApplyTimestamp,
pub(crate) name: SmallString,
pub last_modified: u64,
}
pub struct ChannelRef<T: TxnT> {
pub(crate) r: Rc<RefCell<Channel<T>>>,
}
impl<T: TxnT> Clone for ChannelRef<T> {
fn clone(&self) -> Self {
ChannelRef { r: self.r.clone() }
}
}
impl<T: TxnT> Channel<T> {
pub fn name(&self) -> &str {
self.name.as_str()
}
}
impl<T: TxnT> RemoteRef<T> {
pub fn name(&self) -> &str {
self.name.as_str()
}
}
impl<T: TxnT> ChannelRef<T> {
pub fn borrow(&self) -> std::cell::Ref<Channel<T>> {
self.r.borrow()
}
pub fn borrow_mut(&mut self) -> std::cell::RefMut<Channel<T>> {
self.r.borrow_mut()
}
}
pub struct Remote<T: TxnT> {
pub remote: T::Remote,
pub rev: T::Revremote,
pub states: T::Remotestates,
}
pub struct RemoteRef<T: TxnT> {
db: Rc<RefCell<Remote<T>>>,
name: SmallString,
}
impl<T: TxnT> Clone for RemoteRef<T> {
fn clone(&self) -> Self {
RemoteRef {
db: self.db.clone(),
name: self.name.clone(),
}
}
}
impl<T: TxnT> RemoteRef<T> {
pub fn borrow(&self) -> std::cell::Ref<Remote<T>> {
self.db.borrow()
}
pub fn borrow_mut(&mut self) -> std::cell::RefMut<Remote<T>> {
self.db.borrow_mut()
}
}
pub trait TxnT: Sized {
table!(graph);
cursor_ref!(graph, Vertex<ChangeId>, Edge);
get!(graph, Vertex<ChangeId>, Edge);
table!(changeset);
get!(changeset, ChangeId, u64);
cursor!(changeset, ChangeId, u64);
table!(revchangeset);
get!(revchangeset, u64, (ChangeId, Merkle));
cursor_ref!(revchangeset, u64, (ChangeId, Merkle));
rev_cursor!(revchangeset, u64, (ChangeId, Merkle));
table!(channelstates);
table!(tree);
table_get!(tree, PathId, Inode);
iter!(tree, OwnedPathId, Inode);
table!(revtree);
table_get!(revtree, Inode, PathId);
iter!(revtree, Inode, OwnedPathId);
table!(inodes);
table_get!(inodes, Inode, Position<ChangeId>);
table_get!(revinodes, Position<ChangeId>, Inode);
table!(partials);
cursor!(partials, SmallString, Position<ChangeId>);
table!(channels);
cursor!(channels, SmallString, (u64, u64, u64, u64, u64, u64));
fn iter_partials<'txn>(
&'txn self,
channel: &str,
) -> Cursor<Self, &'txn Self, Self::PartialsCursor, SmallString, Position<ChangeId>>;
table!(revdep);
table!(dep);
table_get!(dep, ChangeId, ChangeId);
cursor_ref!(dep, ChangeId, ChangeId);
table_get!(revdep, ChangeId, ChangeId);
table!(touched_files);
table!(rev_touched_files);
table_get!(touched_files, Position<ChangeId>, ChangeId);
table_get!(rev_touched_files, ChangeId, Position<ChangeId>);
iter!(touched_files, Position<ChangeId>, ChangeId);
iter!(rev_touched_files, ChangeId, Position<ChangeId>);
fn get_external(&self, p: ChangeId) -> Option<Hash>;
fn get_internal(&self, p: Hash) -> Option<ChangeId>;
fn hash_from_prefix(&self, prefix: &str) -> Result<(Hash, ChangeId), anyhow::Error>;
fn hash_from_prefix_remote(
&self,
remote: &RemoteRef<Self>,
prefix: &str,
) -> Result<Hash, anyhow::Error>;
fn load_channel(&self, name: &str) -> Option<ChannelRef<Self>>;
fn load_remote(&self, name: &str) -> Option<RemoteRef<Self>>;
fn iter_channels<'txn>(&'txn self, start: &str) -> ChannelIterator<'txn, Self>;
fn iter_remotes<'txn>(&'txn self, start: &str) -> RemotesIterator<'txn, Self>;
fn iter_revdep<'a, 'txn>(
&'txn self,
p: ChangeId,
) -> Cursor<Self, &'txn Self, Self::DepCursor, ChangeId, ChangeId>;
fn iter_dep<'txn>(
&'txn self,
p: ChangeId,
) -> Cursor<Self, &'txn Self, Self::DepCursor, ChangeId, ChangeId>;
fn iter_dep_ref<RT: std::ops::Deref<Target = Self> + Clone>(
txn: RT,
p: ChangeId,
) -> Cursor<Self, RT, Self::DepCursor, ChangeId, ChangeId>;
fn iter_touched<'txn>(
&'txn self,
p: Position<ChangeId>,
) -> Cursor<Self, &'txn Self, Self::Touched_filesCursor, Position<ChangeId>, ChangeId>;
fn iter_rev_touched<'txn>(
&'txn self,
p: ChangeId,
) -> Cursor<Self, &'txn Self, Self::Rev_touched_filesCursor, ChangeId, Position<ChangeId>>;
table!(remotes);
cursor!(remotes, SmallString, (u64, u64, u64));
table!(remote);
table!(revremote);
table!(remotestates);
cursor!(remote, u64, (Hash, Merkle));
rev_cursor!(remote, u64, (Hash, Merkle));
fn iter_remote<'txn>(
&'txn self,
remote: &Self::Remote,
k: u64,
) -> Cursor<Self, &'txn Self, Self::RemoteCursor, u64, (Hash, Merkle)>;
fn iter_rev_remote<'txn>(
&'txn self,
remote: &Self::Remote,
k: Option<u64>,
) -> RevCursor<Self, &'txn Self, Self::RemoteCursor, u64, (Hash, Merkle)>;
fn get_remote(&mut self, name: &str) -> Option<RemoteRef<Self>>;
fn last_remote(&self, remote: &Self::Remote) -> Option<(u64, (Hash, Merkle))>;
fn get_remote_state(&self, remote: &Self::Remote, n: u64) -> Option<(u64, (Hash, Merkle))>;
fn remote_has_change(&self, remote: &RemoteRef<Self>, hash: Hash) -> bool;
fn remote_has_state(&self, remote: &RemoteRef<Self>, hash: Merkle) -> bool;
fn channel_has_state(&self, channel: &ChannelRef<Self>, hash: Merkle) -> bool;
cursor!(inodes, Inode, Position<ChangeId>);
#[cfg(not(debug_assertions))]
fn debug_inodes(&self) {}
fn iter_inodes<'txn>(
&'txn self,
) -> Cursor<Self, &'txn Self, Self::InodesCursor, Inode, Position<ChangeId>>;
}
pub(crate) fn iter_adjacent<'db, 'txn: 'db, T: TxnT>(
txn: &'txn T,
channel: &'db Channel<T>,
key: Vertex<ChangeId>,
min_flag: EdgeFlags,
max_flag: EdgeFlags,
) -> AdjacentIterator<'txn, T> {
let edge = Edge {
flag: min_flag,
dest: Position::ROOT,
introduced_by: ChangeId::ROOT,
};
AdjacentIterator {
it: iter_graph(txn, &channel.graph, key, Some(edge)),
key,
min_flag,
max_flag,
}
}
pub(crate) fn tree_path<T: TxnT>(txn: &T, v: Position<ChangeId>) -> Option<String> {
if let Some(mut inode) = txn.get_revinodes(v, None) {
let mut components = Vec::new();
while !inode.is_root() {
if let Some(next) = txn.get_revtree(inode, None) {
components.push(next.basename.as_str().to_string());
inode = next.parent_inode;
} else {
assert!(components.is_empty());
return None;
}
}
if let Some(mut result) = components.pop() {
while let Some(c) = components.pop() {
result = result + "/" + c.as_str()
}
Some(result)
} else {
None
}
} else {
None
}
}
pub(crate) fn internal<T: TxnT>(txn: &T, h: &Option<Hash>, p: ChangeId) -> Option<ChangeId> {
match *h {
Some(Hash::None) => Some(ChangeId::ROOT),
Some(h) => txn.get_internal(h),
None => Some(p),
}
}
pub(crate) fn internal_pos<T: TxnT>(
txn: &T,
pos: &Position<Option<Hash>>,
change_id: ChangeId,
) -> Result<Position<ChangeId>, crate::Error> {
Ok(Position {
change: if let Some(p) = pos.change {
if let Some(p) = txn.get_internal(p) {
p
} else {
return Err(crate::Error::InconsistentChange);
}
} else {
change_id
},
pos: pos.pos,
})
}
pub(crate) fn iter_graph<'txn, T: TxnT>(
txn: &'txn T,
graph: &T::Graph,
k: Vertex<ChangeId>,
v: Option<Edge>,
) -> Cursor<T, &'txn T, T::GraphCursor, Vertex<ChangeId>, Edge> {
let curs = txn.cursor_graph(graph, Some((k, v)));
curs
}
pub(crate) fn iter_graph_ref<T: TxnT, RT: std::ops::Deref<Target = T>>(
txn: RT,
graph: &T::Graph,
k: Vertex<ChangeId>,
v: Option<Edge>,
) -> Cursor<T, RT, T::GraphCursor, Vertex<ChangeId>, Edge> {
let curs = T::cursor_graph_ref(txn, graph, Some((k, v)));
curs
}
pub(crate) fn changeid_log<'db, 'txn: 'db, T: TxnT>(
txn: &'txn T,
channel: &'db Channel<T>,
from: u64,
) -> Cursor<T, &'txn T, T::RevchangesetCursor, u64, (ChangeId, Merkle)> {
txn.cursor_revchangeset(&channel.revchanges, Some((from, None)))
}
pub(crate) fn current_state<'db, 'txn: 'db, T: TxnT>(
txn: &'txn T,
channel: &'db Channel<T>,
) -> Option<Merkle> {
txn.rev_cursor_revchangeset(&channel.revchanges, None)
.next()
.map(|(_, (_, m))| m)
}
pub(crate) fn changeid_log_ref<T: TxnT, RT: std::ops::Deref<Target = T>>(
txn: RT,
channel: &Channel<T>,
from: u64,
) -> Cursor<T, RT, T::RevchangesetCursor, u64, (ChangeId, Merkle)> {
T::cursor_revchangeset_ref(txn, &channel.revchanges, Some((from, None)))
}
pub(crate) fn changeid_rev_log<'db, 'txn: 'db, T: TxnT>(
txn: &'txn T,
channel: &'db Channel<T>,
from: Option<u64>,
) -> RevCursor<T, &'txn T, T::RevchangesetCursor, u64, (ChangeId, Merkle)> {
txn.rev_cursor_revchangeset(&channel.revchanges, from.map(|from| (from, None)))
}
pub(crate) fn log_for_path<'txn, 'channel, T: TxnT>(
txn: &'txn T,
channel: &'channel Channel<T>,
key: Position<ChangeId>,
from_timestamp: u64,
) -> PathChangeset<'channel, 'txn, T> {
PathChangeset {
iter: txn.cursor_revchangeset(&channel.revchanges, Some((from_timestamp, None))),
txn,
channel,
key,
}
}
pub(crate) fn rev_log_for_path<'txn, 'channel, T: TxnT>(
txn: &'txn T,
channel: &'channel Channel<T>,
key: Position<ChangeId>,
from_timestamp: u64,
) -> RevPathChangeset<'channel, 'txn, T> {
RevPathChangeset {
iter: txn.rev_cursor_revchangeset(&channel.revchanges, Some((from_timestamp, None))),
txn,
channel,
key,
}
}
pub(crate) fn test_edge<T: TxnT>(
txn: &T,
channel: &Channel<T>,
a: Position<ChangeId>,
b: Position<ChangeId>,
min: EdgeFlags,
max: EdgeFlags,
) -> bool {
debug!("is_connected {:?} {:?}", a, b);
let key = Vertex {
change: a.change,
start: a.pos,
end: a.pos,
};
let edge = Edge {
flag: min,
dest: b,
introduced_by: ChangeId::ROOT,
};
let mut cursor = txn.cursor_graph(&channel.graph, Some((key, Some(edge))));
let (a_, b_) = cursor.next().unwrap();
a_.change == a.change
&& a_.start <= a.pos
&& a_.end >= a.pos
&& b_.flag >= min
&& b_.flag <= max
&& b_.dest == b
}
pub(crate) fn is_alive<T: TxnT>(txn: &T, channel: &Channel<T>, a: Vertex<ChangeId>) -> bool {
a.is_root()
|| iter_adjacent(
txn,
channel,
a,
EdgeFlags::PARENT,
EdgeFlags::all() - EdgeFlags::DELETED,
)
.filter(|e| e.flag.contains(EdgeFlags::BLOCK) || e.flag.contains(EdgeFlags::FOLDER))
.next()
.is_some()
}
pub(crate) fn make_changeid<T: TxnT>(txn: &T, h: &Hash) -> ChangeId {
if let Some(h) = txn.get_internal(*h) {
return h;
}
use byteorder::{ByteOrder, LittleEndian};
use rand::Rng;
let mut p = match h {
Hash::None => return ChangeId::ROOT,
Hash::Blake3(ref s) => ChangeId(LittleEndian::read_u64(&s[..])),
};
while txn.get_external(p).is_some() {
p = ChangeId(rand::thread_rng().gen());
}
p
}
#[cfg(debug_assertions)]
pub fn debug_tree<P: AsRef<std::path::Path>, T: TxnT>(
txn: &T,
file: P,
) -> Result<(), anyhow::Error> {
let root = OwnedPathId {
parent_inode: Inode::ROOT,
basename: SmallString::from_str(""),
};
let mut f = std::fs::File::create(file)?;
for t in txn.iter_tree(root, None) {
writeln!(f, "{:?}", t)?
}
Ok(())
}
#[cfg(debug_assertions)]
pub fn debug_tree_print<T: TxnT>(txn: &T) {
let root = OwnedPathId {
parent_inode: Inode::ROOT,
basename: SmallString::from_str(""),
};
for t in txn.iter_tree(root, None) {
debug!("{:?}", t)
}
}
#[cfg(debug_assertions)]
pub fn debug_to_file<P: AsRef<std::path::Path>, T: TxnT>(
txn: &T,
channel: &ChannelRef<T>,
f: P,
) -> Result<bool, anyhow::Error> {
info!("debug {:?}", f.as_ref());
let mut f = std::fs::File::create(f)?;
let channel = channel.r.borrow();
let done = debug(txn, &channel, &mut f)?;
f.flush()?;
info!("done debugging {:?}", done);
Ok(done)
}
#[cfg(debug_assertions)]
pub fn debug_revtree<P: AsRef<std::path::Path>, T: TxnT>(
txn: &T,
file: P,
) -> Result<(), anyhow::Error> {
let mut f = std::fs::File::create(file)?;
for t in txn.iter_revtree(Inode::ROOT, None) {
writeln!(f, "{:?}", t)?
}
Ok(())
}
#[cfg(debug_assertions)]
pub fn debug_revtree_print<T: TxnT>(txn: &T) {
for t in txn.iter_revtree(Inode::ROOT, None) {
debug!("{:?}", t)
}
}
#[cfg(debug_assertions)]
pub fn debug_inodes<T: TxnT>(txn: &T) {
debug!("debug_inodes");
for t in txn.iter_inodes() {
debug!("debug_inodes = {:?}", t)
}
debug!("/debug_inodes");
}
#[cfg(debug_assertions)]
pub fn debug<W: Write, T: TxnT>(
txn: &T,
channel: &Channel<T>,
mut f: W,
) -> Result<bool, anyhow::Error> {
let mut cursor = txn.cursor_graph(&channel.graph, None);
writeln!(f, "digraph {{")?;
let mut keys = std::collections::HashSet::new();
let mut at_least_one = false;
while let Some((k, v)) = cursor.next() {
at_least_one = true;
debug!("debug {:?} {:?}", k, v);
if keys.insert(k) {
debug_vertex(&mut f, k)?
}
debug_edge(txn, channel, &mut f, k, v)?
}
writeln!(f, "}}")?;
Ok(at_least_one)
}
pub fn check_alive<T: TxnT>(
txn: &T,
channel: &ChannelRef<T>,
) -> (
HashMap<Vertex<ChangeId>, Option<Vertex<ChangeId>>>,
Vec<(Vertex<ChangeId>, Option<Vertex<ChangeId>>)>,
) {
let channel = channel.r.borrow();
let mut reachable = HashSet::new();
let mut stack = vec![Vertex::ROOT];
while let Some(v) = stack.pop() {
if !reachable.insert(v) {
continue;
}
for e in iter_adjacent(
txn,
&channel,
v,
EdgeFlags::empty(),
EdgeFlags::all() - EdgeFlags::DELETED - EdgeFlags::PARENT,
) {
stack.push(find_block(txn, &channel, e.dest).unwrap());
}
}
debug!("reachable = {:#?}", reachable);
let mut alive_unreachable = HashMap::new();
let mut cursor = txn.cursor_graph(&channel.graph, None);
let mut visited = HashSet::new();
let mut k0 = Vertex::ROOT;
let mut k0_has_pseudo_parents = false;
let mut k0_has_regular_parents = false;
let mut reachable_pseudo = Vec::new();
while let Some((k, v)) = cursor.next() {
debug!("check_alive, k = {:?}, v = {:?}", k, v);
if k0 != k {
if k0_has_pseudo_parents && !k0_has_regular_parents {
reachable_pseudo.push((k0, find_file(txn, &channel, k0, &mut stack, &mut visited)))
}
k0 = k;
k0_has_pseudo_parents = false;
k0_has_regular_parents = false;
}
if v.flag.contains(EdgeFlags::PARENT)
&& !v.flag.contains(EdgeFlags::FOLDER)
&& !v.flag.contains(EdgeFlags::DELETED)
{
if v.flag.contains(EdgeFlags::PSEUDO) {
k0_has_pseudo_parents = true
} else {
k0_has_regular_parents = true
}
}
if v.flag.contains(EdgeFlags::PARENT) && !v.flag.contains(EdgeFlags::DELETED) {
if !reachable.contains(&k) {
let file = find_file(txn, &channel, k, &mut stack, &mut visited);
alive_unreachable.insert(k, file);
}
}
}
if !k0.is_root() && k0_has_pseudo_parents && !k0_has_regular_parents {
reachable_pseudo.push((k0, find_file(txn, &channel, k0, &mut stack, &mut visited)));
}
(alive_unreachable, reachable_pseudo)
}
fn find_file<T: TxnT>(
txn: &T,
channel: &Channel<T>,
k: Vertex<ChangeId>,
stack: &mut Vec<Vertex<ChangeId>>,
visited: &mut HashSet<Vertex<ChangeId>>,
) -> Option<Vertex<ChangeId>> {
let mut file = None;
stack.clear();
stack.push(k);
visited.clear();
'outer: while let Some(kk) = stack.pop() {
if !visited.insert(kk) {
continue;
}
for e in iter_adjacent(txn, &channel, kk, EdgeFlags::PARENT, EdgeFlags::all()) {
if e.flag.contains(EdgeFlags::PARENT) {
if e.flag.contains(EdgeFlags::FOLDER) {
file = Some(kk);
break 'outer;
}
stack.push(find_block_end(txn, &channel, e.dest).unwrap());
}
}
}
file
}
pub(crate) fn debug_root<W: Write, T: TxnT>(
txn: &T,
channel: &Channel<T>,
root: Vertex<ChangeId>,
mut f: W,
) -> Result<(), anyhow::Error> {
writeln!(f, "digraph {{")?;
let mut visited = HashSet::new();
let mut stack = vec![root];
while let Some(v) = stack.pop() {
if !visited.insert(v) {
continue;
}
debug_vertex(&mut f, v)?;
for e in iter_adjacent(txn, &channel, v, EdgeFlags::empty(), EdgeFlags::all()) {
if e.flag.contains(EdgeFlags::PARENT) {
debug_edge(txn, &channel, &mut f, v, e)?;
let v = find_block_end(txn, &channel, e.dest).unwrap();
stack.push(v);
}
}
}
writeln!(f, "}}")?;
Ok(())
}
fn debug_vertex<W: std::io::Write>(mut f: W, k: Vertex<ChangeId>) -> Result<(), std::io::Error> {
writeln!(
f,
"node_{}_{}_{}[label=\"{} [{};{}[\"];",
k.change.to_base32(),
k.start.0,
k.end.0,
k.change.to_base32(),
k.start.0,
k.end.0,
)
}
fn debug_edge<T: TxnT, W: std::io::Write>(
txn: &T,
channel: &Channel<T>,
mut f: W,
k: Vertex<ChangeId>,
v: Edge,
) -> Result<(), std::io::Error> {
let style = if v.flag.contains(EdgeFlags::DELETED) {
", style=dashed"
} else if v.flag.contains(EdgeFlags::PSEUDO) {
", style=dotted"
} else {
""
};
let color = if v.flag.contains(EdgeFlags::PARENT) {
if v.flag.contains(EdgeFlags::FOLDER) {
"orange"
} else {
"red"
}
} else if v.flag.contains(EdgeFlags::FOLDER) {
"royalblue"
} else {
"forestgreen"
};
if v.flag.contains(EdgeFlags::PARENT) {
let dest = if v.dest.change.is_root() {
Vertex::ROOT
} else {
find_block_end(txn, channel, v.dest).unwrap()
};
writeln!(
f,
"node_{}_{}_{} -> node_{}_{}_{} [label=\"{}{}{}\", color=\"{}\"{}];",
k.change.to_base32(),
k.start.0,
k.end.0,
dest.change.to_base32(),
dest.start.0,
dest.end.0,
if v.flag.contains(EdgeFlags::BLOCK) {
"["
} else {
""
},
v.introduced_by.to_base32(),
if v.flag.contains(EdgeFlags::BLOCK) {
"]"
} else {
""
},
color,
style
)?;
} else if let Ok(dest) = find_block(txn, &channel, v.dest) {
writeln!(
f,
"node_{}_{}_{} -> node_{}_{}_{} [label=\"{}{}{}\", color=\"{}\"{}];",
k.change.to_base32(),
k.start.0,
k.end.0,
dest.change.to_base32(),
dest.start.0,
dest.end.0,
if v.flag.contains(EdgeFlags::BLOCK) {
"["
} else {
""
},
v.introduced_by.to_base32(),
if v.flag.contains(EdgeFlags::BLOCK) {
"]"
} else {
""
},
color,
style
)?;
} else {
writeln!(
f,
"node_{}_{}_{} -> node_{}_{} [label=\"{}{}{}\", color=\"{}\"{}];",
k.change.to_base32(),
k.start.0,
k.end.0,
v.dest.change.to_base32(),
v.dest.pos.0,
if v.flag.contains(EdgeFlags::BLOCK) {
"["
} else {
""
},
v.introduced_by.to_base32(),
if v.flag.contains(EdgeFlags::BLOCK) {
"]"
} else {
""
},
color,
style
)?;
}
Ok(())
}
pub struct Cursor<T: TxnT, RT: std::ops::Deref<Target = T>, Cursor, K, V> {
pub(crate) cursor: Cursor,
pub(crate) txn: RT,
pub(crate) marker: std::marker::PhantomData<(T, K, V)>,
}
pub struct RevCursor<T: TxnT, RT: std::ops::Deref<Target = T>, Cursor, K, V> {
pub(crate) cursor: Cursor,
pub(crate) txn: RT,
pub(crate) marker: std::marker::PhantomData<(T, K, V)>,
}
initialized_cursor!(graph, Vertex<ChangeId>, Edge);
initialized_cursor!(changeset, ChangeId, u64);
initialized_cursor!(revchangeset, u64, (ChangeId, Merkle));
initialized_rev_cursor!(revchangeset, u64, (ChangeId, Merkle));
initialized_cursor!(tree, OwnedPathId, Inode);
initialized_cursor!(revtree, Inode, OwnedPathId);
initialized_cursor!(dep, ChangeId, ChangeId);
initialized_cursor!(partials, SmallString, Position<ChangeId>);
initialized_cursor!(rev_touched_files, ChangeId, Position<ChangeId>);
initialized_cursor!(touched_files, Position<ChangeId>, ChangeId);
initialized_cursor!(remote, u64, (Hash, Merkle));
initialized_rev_cursor!(remote, u64, (Hash, Merkle));
initialized_cursor!(inodes, Inode, Position<ChangeId>);
pub struct AdjacentIterator<'txn, T: TxnT> {
it: Cursor<T, &'txn T, T::GraphCursor, Vertex<ChangeId>, Edge>,
key: Vertex<ChangeId>,
min_flag: EdgeFlags,
max_flag: EdgeFlags,
}
impl<'txn, T: TxnT> Iterator for AdjacentIterator<'txn, T> {
type Item = Edge;
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some((v, e)) = self.it.next() {
debug!("adjacent iterator: {:?} {:?}", v, e);
if v == self.key {
if e.flag >= self.min_flag {
if e.flag <= self.max_flag {
return Some(e);
} else {
return None;
}
}
} else if v > self.key {
return None;
}
} else {
debug!("adjacent iterator: over");
return None;
}
}
}
}
pub struct PathChangeset<'channel, 'txn: 'channel, T: TxnT> {
txn: &'txn T,
channel: &'channel Channel<T>,
iter: Cursor<T, &'txn T, T::RevchangesetCursor, u64, (ChangeId, Merkle)>,
key: Position<ChangeId>,
}
pub struct RevPathChangeset<'channel, 'txn: 'channel, T: TxnT> {
txn: &'txn T,
channel: &'channel Channel<T>,
iter: RevCursor<T, &'txn T, T::RevchangesetCursor, u64, (ChangeId, Merkle)>,
key: Position<ChangeId>,
}
impl<'channel, 'txn: 'channel, T: TxnT> Iterator for PathChangeset<'channel, 'txn, T> {
type Item = Hash;
fn next(&mut self) -> Option<Self::Item> {
while let Some((_, (changeid, _))) = self.iter.next() {
for (p, touched) in self.txn.iter_rev_touched_files(changeid, None) {
if p > changeid {
break;
} else if p < changeid {
continue;
}
if is_ancestor_of(self.txn, self.channel, self.key, touched) {
return self.txn.get_external(changeid);
}
}
}
None
}
}
impl<'channel, 'txn: 'channel, T: TxnT> Iterator for RevPathChangeset<'channel, 'txn, T> {
type Item = Hash;
fn next(&mut self) -> Option<Self::Item> {
while let Some((_, (changeid, _))) = self.iter.next() {
for (p, touched) in self.txn.iter_rev_touched_files(changeid, None) {
if p > changeid {
break;
} else if p < changeid {
continue;
}
if is_ancestor_of(self.txn, self.channel, self.key, touched) {
return self.txn.get_external(changeid);
}
}
}
None
}
}
fn is_ancestor_of<T: TxnT>(
txn: &T,
channel: &Channel<T>,
a: Position<ChangeId>,
b: Position<ChangeId>,
) -> bool {
let mut stack = vec![b];
let mut visited = std::collections::HashSet::new();
debug!("a = {:?}", a);
while let Some(b) = stack.pop() {
debug!("pop {:?}", b);
if a == b {
return true;
}
if !visited.insert(b) {
continue;
}
for p in iter_adjacent(
txn,
channel,
b.inode_vertex(),
EdgeFlags::FOLDER | EdgeFlags::PARENT,
EdgeFlags::FOLDER | EdgeFlags::PARENT | EdgeFlags::PSEUDO,
) {
let parent = find_block_end(txn, channel, p.dest).unwrap();
for pp in iter_adjacent(
txn,
channel,
parent,
EdgeFlags::FOLDER | EdgeFlags::PARENT,
EdgeFlags::FOLDER | EdgeFlags::PARENT | EdgeFlags::PSEUDO,
) {
if pp.dest == a {
return true;
}
stack.push(pp.dest)
}
}
}
false
}
pub struct ChannelIterator<'txn, T: TxnT> {
txn: &'txn T,
cursor: T::ChannelsCursor,
}
impl<'txn, T: TxnT> Iterator for ChannelIterator<'txn, T> {
type Item = ChannelRef<T>;
fn next(&mut self) -> Option<Self::Item> {
let next: Option<(SmallString, (u64, u64, u64, u64, u64, u64))> =
self.txn.cursor_channels_next(&mut self.cursor);
if let Some((name, _)) = next {
self.txn.load_channel(name.as_str())
} else {
None
}
}
}
pub struct RemotesIterator<'txn, T: TxnT> {
txn: &'txn T,
cursor: T::RemotesCursor,
}
impl<'txn, T: TxnT> Iterator for RemotesIterator<'txn, T> {
type Item = RemoteRef<T>;
fn next(&mut self) -> Option<Self::Item> {
let next: Option<(SmallString, (u64, u64, u64))> =
self.txn.cursor_remotes_next(&mut self.cursor);
if let Some((name, _)) = next {
self.txn.load_remote(name.as_str())
} else {
None
}
}
}
pub trait MutTxnT: TxnT {
put_del!(internal, Hash, ChangeId);
put_del!(external, ChangeId, Hash);
put_del!(inodes, Inode, Position<ChangeId>);
put_del!(revinodes, Position<ChangeId>, Inode);
put_del!(tree, PathId, Inode);
put_del!(revtree, Inode, PathId);
put_del!(dep, ChangeId, ChangeId);
put_del!(revdep, ChangeId, ChangeId);
put_del!(touched_files, Position<ChangeId>, ChangeId);
put_del!(rev_touched_files, ChangeId, Position<ChangeId>);
fn put_graph(
&mut self,
channel: &mut Self::Graph,
k: Vertex<ChangeId>,
v: Edge,
) -> Result<bool, anyhow::Error>;
fn del_graph(
&mut self,
channel: &mut Self::Graph,
k: Vertex<ChangeId>,
v: Option<Edge>,
) -> Result<bool, anyhow::Error>;
fn put_changes(
&mut self,
channel: &mut Channel<Self>,
p: ChangeId,
t: ApplyTimestamp,
h: &Hash,
) -> Result<Option<Merkle>, anyhow::Error>;
fn del_changes(
&mut self,
channel: &mut Channel<Self>,
p: ChangeId,
t: ApplyTimestamp,
) -> Result<bool, anyhow::Error>;
fn open_or_create_channel(&mut self, name: &str) -> Result<ChannelRef<Self>, anyhow::Error>;
fn fork(
&mut self,
channel: &ChannelRef<Self>,
name: &str,
) -> Result<ChannelRef<Self>, anyhow::Error>;
fn rename_channel(
&mut self,
channel: &mut ChannelRef<Self>,
name: &str,
) -> Result<(), anyhow::Error>;
fn drop_channel(&mut self, name: &str) -> Result<bool, anyhow::Error>;
fn commit(self) -> Result<(), anyhow::Error>;
fn put_partials(&mut self, k: &str, e: Position<ChangeId>) -> Result<bool, anyhow::Error>;
fn del_partials(
&mut self,
k: &str,
e: Option<Position<ChangeId>>,
) -> Result<bool, anyhow::Error>;
fn open_or_create_remote(&mut self, name: &str) -> Result<RemoteRef<Self>, anyhow::Error>;
fn put_remote(
&mut self,
remote: &mut RemoteRef<Self>,
k: u64,
v: (Hash, Merkle),
) -> Result<bool, anyhow::Error>;
fn del_remote(&mut self, remote: &mut RemoteRef<Self>, k: u64) -> Result<bool, anyhow::Error>;
fn drop_remote(&mut self, remote: RemoteRef<Self>) -> Result<bool, anyhow::Error>;
fn drop_named_remote(&mut self, remote: &str) -> Result<bool, anyhow::Error>;
}
pub(crate) fn put_inodes_with_rev<T: MutTxnT>(
txn: &mut T,
inode: Inode,
position: Position<ChangeId>,
) -> Result<(), anyhow::Error> {
txn.put_inodes(inode, position)?;
txn.put_revinodes(position, inode)?;
Ok(())
}
pub(crate) fn split_block<T: MutTxnT>(
txn: &mut T,
channel: &mut Channel<T>,
key: Vertex<ChangeId>,
pos: ChangePosition,
) -> Result<(), anyhow::Error> {
trace!("key = {:?}, pos = {:?}", key, pos);
let adjacent: Vec<_> = txn
.cursor_graph(&channel.graph, Some((key, None)))
.take_while(|&(k, _)| k <= key)
.filter(|&(k, _)| k == key)
.map(|(_, e)| e)
.collect();
debug!("adjacent {:?}", adjacent);
for chi in adjacent {
assert!(chi.introduced_by != ChangeId::ROOT || chi.flag.contains(EdgeFlags::PSEUDO));
if chi.flag.contains(EdgeFlags::PARENT | EdgeFlags::BLOCK) {
put_graph_with_rev(
txn,
channel,
chi.flag - EdgeFlags::PARENT,
Vertex {
change: key.change,
start: key.start,
end: pos,
},
Vertex {
change: key.change,
start: pos,
end: key.end,
},
chi.introduced_by,
)?;
}
txn.del_graph(&mut channel.graph, key, Some(chi))?;
txn.put_graph(
&mut channel.graph,
if chi.flag.contains(EdgeFlags::PARENT) {
Vertex {
change: key.change,
start: key.start,
end: pos,
}
} else {
Vertex {
change: key.change,
start: pos,
end: key.end,
}
},
chi,
)?;
}
Ok(())
}
pub(crate) fn del_graph_with_rev<T: MutTxnT>(
txn: &mut T,
channel: &mut Channel<T>,
mut flag: EdgeFlags,
mut k0: Vertex<ChangeId>,
mut k1: Vertex<ChangeId>,
introduced_by: ChangeId,
) -> Result<bool, anyhow::Error> {
if flag.contains(EdgeFlags::PARENT) {
std::mem::swap(&mut k0, &mut k1);
flag -= EdgeFlags::PARENT
}
debug!("del_graph_with_rev {:?} {:?} {:?}", flag, k0, k1);
let a = txn.del_graph(
&mut channel.graph,
k0,
Some(Edge {
flag: flag,
dest: Position {
change: k1.change,
pos: k1.start,
},
introduced_by,
}),
)?;
let b = txn.del_graph(
&mut channel.graph,
k1,
Some(Edge {
flag: flag | EdgeFlags::PARENT,
dest: Position {
change: k0.change,
pos: k0.end,
},
introduced_by,
}),
)?;
assert!((a && b) || (!a && !b));
Ok(a && b)
}
pub(crate) fn put_graph_with_rev<T: MutTxnT>(
txn: &mut T,
channel: &mut Channel<T>,
flag: EdgeFlags,
k0: Vertex<ChangeId>,
k1: Vertex<ChangeId>,
introduced_by: ChangeId,
) -> Result<bool, anyhow::Error> {
debug_assert!(!flag.contains(EdgeFlags::PARENT));
if k0.change == k1.change {
assert_ne!(k0.start_pos(), k1.start_pos());
}
if introduced_by == ChangeId::ROOT {
assert!(flag.contains(EdgeFlags::PSEUDO));
}
debug!("put_graph_with_rev {:?} {:?} {:?}", k0, k1, flag);
let a = txn.put_graph(
&mut channel.graph,
k0,
Edge {
flag: flag,
dest: Position {
change: k1.change,
pos: k1.start,
},
introduced_by,
},
)?;
let b = txn.put_graph(
&mut channel.graph,
k1,
Edge {
flag: flag ^ EdgeFlags::PARENT,
dest: Position {
change: k0.change,
pos: k0.end,
},
introduced_by,
},
)?;
assert!((a && b) || (!a && !b));
Ok(a && b)
}
pub(crate) fn register_change<T: MutTxnT>(
txn: &mut T,
internal: ChangeId,
hash: Hash,
change: &Change,
) -> Result<(), anyhow::Error> {
txn.put_external(internal, hash)?;
txn.put_internal(hash, internal)?;
for dep in change.dependencies.iter() {
debug!(target:"libpijul::register_change", "dep = {:?}", dep);
let dep_internal = txn.get_internal(*dep).unwrap();
debug!(target:"libpijul::register_change", "{:?} depends on {:?}", internal, dep_internal);
txn.put_revdep(dep_internal, internal)?;
txn.put_dep(internal, dep_internal)?;
}
for hunk in change.changes.iter().flat_map(|r| r.iter()) {
let inode = match *hunk {
Atom::NewVertex(NewVertex { ref inode, .. }) => inode,
Atom::EdgeMap(EdgeMap { ref inode, .. }) => inode,
};
let inode = Position {
change: inode
.change
.and_then(|change| txn.get_internal(change))
.unwrap_or(internal),
pos: inode.pos,
};
debug!(target:"libpijul::register_change", "touched: {:?} {:?}", inode, internal);
txn.put_touched_files(inode, internal)?;
txn.put_rev_touched_files(internal, inode)?;
}
Ok(())
}
pub fn check_tree_inodes<T: TxnT>(txn: &T, channel: &Channel<T>) {
for (inode, vertex) in txn.iter_inodes() {
let mut inode_ = inode;
while !inode_.is_root() {
if let Some(next) = txn.get_revtree(inode_, None) {
inode_ = next.parent_inode;
} else {
panic!("inode = {:?}, inode_ = {:?}", inode, inode_);
}
}
if !is_alive(txn, &channel, vertex.inode_vertex()) {
for e in iter_adjacent(
txn,
&channel,
vertex.inode_vertex(),
EdgeFlags::empty(),
EdgeFlags::all(),
) {
error!("{:?} {:?} {:?}", inode, vertex, e)
}
panic!(
"inode {:?}, vertex {:?}, is not alive, {:?}",
inode,
vertex,
tree_path(txn, vertex)
)
}
}
let mut h = HashMap::new();
let id0 = OwnedPathId {
parent_inode: Inode::ROOT,
basename: crate::small_string::SmallString::new(),
};
for (id, inode) in txn.iter_tree(id0, None) {
if let Some(inode_) = h.insert(id.clone(), inode) {
panic!("id {:?} maps to two inodes: {:?} {:?}", id, inode, inode_);
}
}
}
pub fn check_alive_debug<T: TxnT, C: crate::changestore::ChangeStore>(
changes: &C,
txn: &T,
channel: &ChannelRef<T>,
line: u32,
) -> Result<(), anyhow::Error> {
let (alive, reachable) = crate::pristine::check_alive(txn, &channel);
let mut h = HashSet::new();
if !alive.is_empty() {
for (k, file) in alive.iter() {
debug!("alive = {:?}, file = {:?}", k, file);
h.insert(file);
}
}
if !reachable.is_empty() {
for (k, file) in reachable.iter() {
debug!("reachable = {:?}, file = {:?}", k, file);
h.insert(file);
}
}
for file in h.iter() {
let file_ = file.unwrap().start_pos();
let mut f = std::fs::File::create(&format!("debug_{:?}", file_))?;
let graph = crate::alive::retrieve::retrieve(txn, &channel.borrow(), file_);
graph.debug(changes, txn, &channel.borrow(), false, false, &mut f)?;
let mut f = std::fs::File::create(&format!("debug_all_{:?}", file_))?;
let channel = channel.borrow();
debug_root(txn, &channel, file.unwrap(), &mut f)?;
}
if !h.is_empty() {
panic!("alive call line {}", line);
}
Ok(())
}