use hex;
use rand;
use sanakirja;
use sanakirja::Representable;
pub use sanakirja::Transaction;
use std;
use std::path::Path;
use {Error, Result};
pub use self::patch_id::*;
fn from_hex(hex: &str, s: &mut [u8]) -> bool {
let hex = hex.as_bytes();
if hex.len() <= 2 * s.len() {
let mut i = 0;
while i < hex.len() {
let h = hex[i].to_ascii_lowercase();
if h >= b'0' && h <= b'9' {
s[i / 2] = s[i / 2] << 4 | (h - b'0')
} else if h >= b'a' && h <= b'f' {
s[i / 2] = s[i / 2] << 4 | (h - b'a' + 10)
} else {
return false;
}
i += 1
}
if i & 1 == 1 {
s[i / 2] = s[i / 2] << 4
}
true
} else {
false
}
}
mod edge;
mod file_header;
mod file_id;
mod hash;
mod inode;
mod key;
mod patch_id;
mod small_string;
pub use self::edge::*;
pub use self::file_header::*;
pub use self::file_id::*;
pub use self::hash::*;
pub use self::inode::*;
pub use self::key::*;
pub use self::small_string::*;
pub type NodesDb = sanakirja::Db<self::key::Key<PatchId>, self::edge::Edge>;
pub type ApplyTimestamp = u64;
type PatchSet = sanakirja::Db<self::patch_id::PatchId, ApplyTimestamp>;
type RevPatchSet = sanakirja::Db<ApplyTimestamp, self::patch_id::PatchId>;
pub struct Dbs {
tree: sanakirja::Db<self::file_id::UnsafeFileId, self::inode::Inode>,
revtree: sanakirja::Db<self::inode::Inode, self::file_id::UnsafeFileId>,
inodes: sanakirja::Db<self::inode::Inode, self::file_header::FileHeader>,
revinodes: sanakirja::Db<self::key::Key<PatchId>, self::inode::Inode>,
contents: sanakirja::Db<self::key::Key<PatchId>, sanakirja::value::UnsafeValue>,
internal: sanakirja::Db<self::hash::UnsafeHash, self::patch_id::PatchId>,
external: sanakirja::Db<self::patch_id::PatchId, self::hash::UnsafeHash>,
revdep: sanakirja::Db<self::patch_id::PatchId, self::patch_id::PatchId>,
branches:
sanakirja::Db<self::small_string::UnsafeSmallStr, (NodesDb, PatchSet, RevPatchSet, u64)>,
cemetery: sanakirja::Db<(self::key::Key<PatchId>, self::edge::Edge), self::patch_id::PatchId>,
dep: sanakirja::Db<self::patch_id::PatchId, self::patch_id::PatchId>,
touched_files: sanakirja::Db<self::key::Key<PatchId>, self::patch_id::PatchId>,
partials: sanakirja::Db<self::small_string::UnsafeSmallStr, self::key::Key<PatchId>>,
}
pub struct GenericTxn<T, R> {
#[doc(hidden)]
pub txn: T,
#[doc(hidden)]
pub rng: R,
#[doc(hidden)]
pub dbs: Dbs,
}
pub type MutTxn<'env, R> = GenericTxn<sanakirja::MutTxn<'env, ()>, R>;
pub type Txn<'env> = GenericTxn<sanakirja::Txn<'env>, ()>;
pub const DEFAULT_BRANCH: &'static str = "master";
pub struct Repository {
pub env: sanakirja::Env,
}
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum Root {
Tree,
RevTree,
Inodes,
RevInodes,
Contents,
Internal,
External,
RevDep,
Branches,
Cemetery,
TouchedFiles,
Dep,
RevTouchedFiles,
Partials,
}
trait OpenDb: Transaction {
fn open_db<K: Representable, V: Representable>(
&mut self,
num: Root,
) -> Result<sanakirja::Db<K, V>> {
if let Some(db) = self.root(num as usize) {
Ok(db)
} else {
Err(Error::NoDb(num))
}
}
}
impl<'a, T> OpenDb for sanakirja::MutTxn<'a, T> {
fn open_db<K: Representable, V: Representable>(
&mut self,
num: Root,
) -> Result<sanakirja::Db<K, V>> {
if let Some(db) = self.root(num as usize) {
Ok(db)
} else {
Ok(self.create_db()?)
}
}
}
impl<'a> OpenDb for sanakirja::Txn<'a> {}
const MIN_REPO_SIZE: u64 = 1 << 17;
impl Repository {
#[doc(hidden)]
pub fn size(&self) -> u64 {
self.env.size()
}
#[doc(hidden)]
pub fn repository_size<P: AsRef<Path>>(path: P) -> Result<u64> {
let size = sanakirja::Env::file_size(path.as_ref())?;
debug!("repository_size = {:?}", size);
Ok(size)
}
pub fn open<P: AsRef<Path>>(path: P, size_increase: Option<u64>) -> Result<Self> {
let size = if let Some(size) = size_increase {
Repository::repository_size(path.as_ref()).unwrap_or(MIN_REPO_SIZE)
+ std::cmp::max(size, MIN_REPO_SIZE)
} else {
if let Ok(len) = Repository::repository_size(path.as_ref()) {
std::cmp::max(len, MIN_REPO_SIZE)
} else {
MIN_REPO_SIZE
}
};
Ok(Repository {
env: sanakirja::Env::new(path, size)?,
})
}
pub unsafe fn open_nolock<P: AsRef<Path>>(path: P, size_increase: Option<u64>) -> Result<Self> {
let size = if let Some(size) = size_increase {
Repository::repository_size(path.as_ref()).unwrap_or(MIN_REPO_SIZE)
+ std::cmp::max(size, MIN_REPO_SIZE)
} else {
if let Ok(len) = Repository::repository_size(path.as_ref()) {
std::cmp::max(len, MIN_REPO_SIZE)
} else {
MIN_REPO_SIZE
}
};
debug!("sanakirja::Env::new_nolock");
Ok(Repository {
env: sanakirja::Env::new_nolock(path, size)?,
})
}
pub unsafe fn close(&mut self) {
self.env.close()
}
pub fn txn_begin(&self) -> Result<Txn> {
let mut txn = self.env.txn_begin()?;
let dbs = Dbs::new(&mut txn)?;
let repo = GenericTxn {
txn: txn,
rng: (),
dbs: dbs,
};
Ok(repo)
}
pub fn mut_txn_begin<R: rand::Rng>(&self, r: R) -> Result<MutTxn<R>> {
let mut txn = self.env.mut_txn_begin()?;
let dbs = Dbs::new(&mut txn)?;
let repo = GenericTxn {
txn: txn,
rng: r,
dbs: dbs,
};
Ok(repo)
}
}
impl Dbs {
fn new<T: OpenDb>(txn: &mut T) -> Result<Self> {
let external = txn.open_db(Root::External)?;
let branches = txn.open_db(Root::Branches)?;
let tree = txn.open_db(Root::Tree)?;
let revtree = txn.open_db(Root::RevTree)?;
let inodes = txn.open_db(Root::Inodes)?;
let revinodes = txn.open_db(Root::RevInodes)?;
let internal = txn.open_db(Root::Internal)?;
let contents = txn.open_db(Root::Contents)?;
let revdep = txn.open_db(Root::RevDep)?;
let cemetery = txn.open_db(Root::Cemetery)?;
let dep = txn.open_db(Root::Dep)?;
let touched_files = txn.open_db(Root::TouchedFiles)?;
let partials = txn.open_db(Root::Partials)?;
Ok(Dbs {
external,
branches,
inodes,
tree,
revtree,
revinodes,
internal,
revdep,
contents,
cemetery,
dep,
touched_files,
partials,
})
}
}
#[derive(Debug)]
pub struct Branch {
pub db: NodesDb,
pub patches: PatchSet,
pub revpatches: RevPatchSet,
pub apply_counter: u64,
pub name: small_string::SmallString,
}
use sanakirja::Commit;
impl<'env, R: rand::Rng> MutTxn<'env, R> {
pub fn open_branch<'name>(&mut self, name: &str) -> Result<Branch> {
let name = small_string::SmallString::from_str(name);
let (branch, patches, revpatches, counter) = if let Some(x) =
self.txn
.get(&self.dbs.branches, name.as_small_str().to_unsafe(), None)
{
x
} else {
(
self.txn.create_db()?,
self.txn.create_db()?,
self.txn.create_db()?,
0,
)
};
Ok(Branch {
db: branch,
patches: patches,
revpatches: revpatches,
name: name,
apply_counter: counter,
})
}
pub fn commit_branch(&mut self, branch: Branch) -> Result<()> {
debug!("Commit_branch. This is not too safe.");
let mut dbs_branches: sanakirja::Db<UnsafeSmallStr, (u64, u64, u64, u64)> =
unsafe { std::mem::transmute(self.dbs.branches) };
debug!("Commit_branch, dbs_branches = {:?}", dbs_branches);
self.txn.del(
&mut self.rng,
&mut dbs_branches,
branch.name.as_small_str().to_unsafe(),
None,
)?;
debug!("Commit_branch, dbs_branches = {:?}", dbs_branches);
self.dbs.branches = unsafe { std::mem::transmute(dbs_branches) };
self.txn.put(
&mut self.rng,
&mut self.dbs.branches,
branch.name.as_small_str().to_unsafe(),
(
branch.db,
branch.patches,
branch.revpatches,
branch.apply_counter,
),
)?;
debug!("Commit_branch, self.dbs.branches = {:?}", self.dbs.branches);
Ok(())
}
pub fn rename_branch(&mut self, branch: &mut Branch, new_name: &str) -> Result<()> {
debug!("Commit_branch. This is not too safe.");
let name_exists = self.get_branch(new_name).is_some();
if name_exists {
Err(Error::BranchNameAlreadyExists(new_name.to_string()))
} else {
let mut dbs_branches: sanakirja::Db<UnsafeSmallStr, (u64, u64, u64, u64)> =
unsafe { std::mem::transmute(self.dbs.branches) };
self.txn.del(
&mut self.rng,
&mut dbs_branches,
branch.name.as_small_str().to_unsafe(),
None,
)?;
self.dbs.branches = unsafe { std::mem::transmute(dbs_branches) };
branch.name.clone_from_str(new_name);
Ok(())
}
}
pub fn commit(mut self) -> Result<()> {
self.txn.set_root(Root::Tree as usize, self.dbs.tree);
self.txn.set_root(Root::RevTree as usize, self.dbs.revtree);
self.txn.set_root(Root::Inodes as usize, self.dbs.inodes);
self.txn
.set_root(Root::RevInodes as usize, self.dbs.revinodes);
self.txn
.set_root(Root::Contents as usize, self.dbs.contents);
self.txn
.set_root(Root::Internal as usize, self.dbs.internal);
self.txn
.set_root(Root::External as usize, self.dbs.external);
self.txn
.set_root(Root::Branches as usize, self.dbs.branches);
self.txn.set_root(Root::RevDep as usize, self.dbs.revdep);
self.txn
.set_root(Root::Cemetery as usize, self.dbs.cemetery);
self.txn.set_root(Root::Dep as usize, self.dbs.dep);
self.txn
.set_root(Root::TouchedFiles as usize, self.dbs.touched_files);
self.txn
.set_root(Root::Partials as usize, self.dbs.partials);
self.txn.commit()?;
Ok(())
}
}
use sanakirja::value::*;
use sanakirja::{Cursor, RevCursor};
pub struct TreeIterator<'a, T: Transaction + 'a>(Cursor<'a, T, UnsafeFileId, Inode>);
impl<'a, T: Transaction + 'a> Iterator for TreeIterator<'a, T> {
type Item = (FileId<'a>, Inode);
fn next(&mut self) -> Option<Self::Item> {
debug!("tree iter");
if let Some((k, v)) = self.0.next() {
debug!("tree iter: {:?} {:?}", k, v);
unsafe { Some((FileId::from_unsafe(k), v)) }
} else {
None
}
}
}
pub struct RevtreeIterator<'a, T: Transaction + 'a>(Cursor<'a, T, Inode, UnsafeFileId>);
impl<'a, T: Transaction + 'a> Iterator for RevtreeIterator<'a, T> {
type Item = (Inode, FileId<'a>);
fn next(&mut self) -> Option<Self::Item> {
if let Some((k, v)) = self.0.next() {
unsafe { Some((k, FileId::from_unsafe(v))) }
} else {
None
}
}
}
pub struct NodesIterator<'a, T: Transaction + 'a>(Cursor<'a, T, Key<PatchId>, Edge>);
impl<'a, T: Transaction> Iterator for NodesIterator<'a, T> {
type Item = (Key<PatchId>, Edge);
fn next(&mut self) -> Option<Self::Item> {
if let Some((k, v)) = self.0.next() {
Some((k, v))
} else {
None
}
}
}
pub struct BranchIterator<'a, T: Transaction + 'a>(
Cursor<'a, T, UnsafeSmallStr, (NodesDb, PatchSet, RevPatchSet, u64)>,
);
impl<'a, T: Transaction + 'a> Iterator for BranchIterator<'a, T> {
type Item = Branch;
fn next(&mut self) -> Option<Self::Item> {
if let Some((k, v)) = self.0.next() {
unsafe {
Some(Branch {
name: SmallStr::from_unsafe(k).to_owned(),
db: v.0,
patches: v.1,
revpatches: v.2,
apply_counter: v.3,
})
}
} else {
None
}
}
}
pub struct PatchesIterator<'a, T: Transaction + 'a>(Cursor<'a, T, PatchId, ApplyTimestamp>);
impl<'a, T: Transaction + 'a> Iterator for PatchesIterator<'a, T> {
type Item = (PatchId, ApplyTimestamp);
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
}
pub struct RevAppliedIterator<'a, T: Transaction + 'a>(RevCursor<'a, T, ApplyTimestamp, PatchId>);
impl<'a, T: Transaction + 'a> Iterator for RevAppliedIterator<'a, T> {
type Item = (ApplyTimestamp, PatchId);
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
}
pub struct AppliedIterator<'a, T: Transaction + 'a>(Cursor<'a, T, ApplyTimestamp, PatchId>);
impl<'a, T: Transaction + 'a> Iterator for AppliedIterator<'a, T> {
type Item = (ApplyTimestamp, PatchId);
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
}
pub struct InodesIterator<'a, T: Transaction + 'a>(Cursor<'a, T, Inode, FileHeader>);
impl<'a, T: Transaction + 'a> Iterator for InodesIterator<'a, T> {
type Item = (Inode, FileHeader);
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
}
pub struct InternalIterator<'a, T: Transaction + 'a>(Cursor<'a, T, UnsafeHash, PatchId>);
impl<'a, T: Transaction + 'a> Iterator for InternalIterator<'a, T> {
type Item = (HashRef<'a>, PatchId);
fn next(&mut self) -> Option<Self::Item> {
if let Some((k, v)) = self.0.next() {
unsafe { Some((HashRef::from_unsafe(k), v)) }
} else {
None
}
}
}
pub struct ExternalIterator<'a, T: Transaction + 'a>(Cursor<'a, T, PatchId, UnsafeHash>);
impl<'a, T: Transaction + 'a> Iterator for ExternalIterator<'a, T> {
type Item = (PatchId, HashRef<'a>);
fn next(&mut self) -> Option<Self::Item> {
if let Some((k, v)) = self.0.next() {
unsafe { Some((k, HashRef::from_unsafe(v))) }
} else {
None
}
}
}
pub struct RevdepIterator<'a, T: Transaction + 'a>(Cursor<'a, T, PatchId, PatchId>);
impl<'a, T: Transaction + 'a> Iterator for RevdepIterator<'a, T> {
type Item = (PatchId, PatchId);
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
}
pub struct DepIterator<'a, T: Transaction + 'a>(Cursor<'a, T, PatchId, PatchId>);
impl<'a, T: Transaction + 'a> Iterator for DepIterator<'a, T> {
type Item = (PatchId, PatchId);
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
}
pub struct ContentsIterator<'a, T: Transaction + 'a>(
&'a T,
Cursor<'a, T, Key<PatchId>, UnsafeValue>,
);
impl<'a, T: Transaction + 'a> Iterator for ContentsIterator<'a, T> {
type Item = (Key<PatchId>, Value<'a, T>);
fn next(&mut self) -> Option<Self::Item> {
if let Some((k, v)) = self.1.next() {
unsafe { Some((k, Value::from_unsafe(&v, self.0))) }
} else {
None
}
}
}
pub struct CemeteryIterator<'a, T: Transaction + 'a>(Cursor<'a, T, (Key<PatchId>, Edge), PatchId>);
impl<'a, T: Transaction + 'a> Iterator for CemeteryIterator<'a, T> {
type Item = ((Key<PatchId>, Edge), PatchId);
fn next(&mut self) -> Option<Self::Item> {
if let Some(((k, v), w)) = self.0.next() {
Some(((k, v), w))
} else {
None
}
}
}
pub struct TouchedIterator<'a, T: Transaction + 'a>(Cursor<'a, T, Key<PatchId>, PatchId>);
impl<'a, T: Transaction + 'a> Iterator for TouchedIterator<'a, T> {
type Item = (Key<PatchId>, PatchId);
fn next(&mut self) -> Option<Self::Item> {
if let Some((k, v)) = self.0.next() {
Some((k, v))
} else {
None
}
}
}
pub struct PartialsIterator<'a, T: Transaction + 'a>(Cursor<'a, T, UnsafeSmallStr, Key<PatchId>>);
impl<'a, T: Transaction + 'a> Iterator for PartialsIterator<'a, T> {
type Item = (SmallStr<'a>, Key<PatchId>);
fn next(&mut self) -> Option<Self::Item> {
if let Some((k, v)) = self.0.next() {
unsafe { Some((SmallStr::from_unsafe(k), v)) }
} else {
None
}
}
}
mod dump {
use super::*;
use sanakirja;
impl<U: Transaction, R> GenericTxn<U, R> {
pub fn dump(&self) {
debug!("============= dumping Tree");
for (k, v) in self.iter_tree(None) {
debug!("> {:?} {:?}", k, v)
}
debug!("============= dumping Inodes");
for (k, v) in self.iter_inodes(None) {
debug!("> {:?} {:?}", k, v)
}
debug!("============= dumping RevDep");
for (k, v) in self.iter_revdep(None) {
debug!("> {:?} {:?}", k, v)
}
debug!("============= dumping Internal");
for (k, v) in self.iter_internal(None) {
debug!("> {:?} {:?}", k, v)
}
debug!("============= dumping External");
for (k, v) in self.iter_external(None) {
debug!("> {:?} {:?} {:?}", k, v, v.to_base58());
}
debug!("============= dumping Contents");
{
sanakirja::debug(&self.txn, &[&self.dbs.contents], "dump_contents", true);
}
debug!("============= dumping Partials");
for (k, v) in self.iter_partials("") {
debug!("> {:?} {:?}", k, v);
}
debug!("============= dumping Branches");
for (br, (db, patches, revpatches, counter)) in self.txn.iter(&self.dbs.branches, None)
{
debug!("patches: {:?} {:?}", patches, revpatches);
debug!(
"============= dumping Patches in branch {:?}, counter = {:?}",
br, counter
);
for (k, v) in self.txn.iter(&patches, None) {
debug!("> {:?} {:?}", k, v)
}
debug!("============= dumping RevPatches in branch {:?}", br);
for (k, v) in self.txn.iter(&revpatches, None) {
debug!("> {:?} {:?}", k, v)
}
debug!("============= dumping Nodes in branch {:?}", br);
unsafe {
debug!("> {:?}", SmallStr::from_unsafe(br));
for (k, v) in self.txn.iter(&db, None) {
debug!(">> {:?} {:?}", k, v)
}
}
}
}
}
}
pub struct AdjIterator<'a, U: Transaction + 'a> {
it: NodesIterator<'a, U>,
key: Key<PatchId>,
max_flag: EdgeFlags,
}
impl<'a, U: Transaction + 'a> Iterator for AdjIterator<'a, U> {
type Item = Edge;
fn next(&mut self) -> Option<Self::Item> {
if let Some((v, e)) = self.it.next() {
if v == self.key && e.flag <= self.max_flag {
Some(e)
} else {
None
}
} else {
None
}
}
}
use std::collections::{BTreeMap, HashSet};
#[derive(Debug)]
pub struct PageCounts {
pub tree: usize,
pub revtree: usize,
pub inodes: usize,
pub revinodes: usize,
pub contents: usize,
pub internal: usize,
pub external: usize,
pub revdep: usize,
pub branch_table: usize,
pub cemetery: usize,
pub dep: usize,
pub touched_files: usize,
pub partials: usize,
pub branches: BTreeMap<String, usize>,
}
impl PageCounts {
pub fn sum(&self) -> usize {
self.tree
+ self.revtree
+ self.inodes
+ self.revinodes
+ self.contents
+ self.internal
+ self.external
+ self.revdep
+ self.branch_table
+ self.cemetery
+ self.dep
+ self.touched_files
+ self.partials
+ self.branches.iter().map(|x| x.1).sum::<usize>()
}
}
impl<'a> Txn<'a> {
pub fn page_counts(&self) -> PageCounts {
let mut references = HashSet::new();
self.txn.references(&mut references, self.dbs.tree);
let tree = references.len();
references.clear();
self.txn.references(&mut references, self.dbs.revtree);
let revtree = references.len();
references.clear();
self.txn.references(&mut references, self.dbs.inodes);
let inodes = references.len();
references.clear();
self.txn.references(&mut references, self.dbs.revinodes);
let revinodes = references.len();
references.clear();
self.txn.references(&mut references, self.dbs.contents);
let contents = references.len();
references.clear();
self.txn.references(&mut references, self.dbs.internal);
let internal = references.len();
references.clear();
self.txn.references(&mut references, self.dbs.external);
let external = references.len();
references.clear();
self.txn.references(&mut references, self.dbs.revdep);
let revdep = references.len();
references.clear();
self.txn.references(&mut references, self.dbs.branches);
let branch_table = references.len();
references.clear();
self.txn.references(&mut references, self.dbs.cemetery);
let cemetery = references.len();
references.clear();
self.txn.references(&mut references, self.dbs.dep);
let dep = references.len();
references.clear();
self.txn.references(&mut references, self.dbs.touched_files);
let touched_files = references.len();
references.clear();
self.txn.references(&mut references, self.dbs.partials);
let partials = references.len();
let mut branches = BTreeMap::new();
for br in self.iter_branches(None) {
references.clear();
self.txn.references(&mut references, br.db);
branches.insert(br.name.as_str().to_string(), references.len());
}
PageCounts {
tree,
revtree,
inodes,
revinodes,
contents,
internal,
external,
revdep,
branch_table,
cemetery,
dep,
touched_files,
partials,
branches,
}
}
}
impl<U: Transaction, R> GenericTxn<U, R> {
pub fn has_branch(&self, name: &str) -> bool {
let name = small_string::SmallString::from_str(name);
self.txn
.get(&self.dbs.branches, name.as_small_str().to_unsafe(), None)
.is_some()
}
pub fn get_branch<'name>(&self, name: &str) -> Option<Branch> {
let name = small_string::SmallString::from_str(name);
if let Some((branch, patches, revpatches, counter)) =
self.txn
.get(&self.dbs.branches, name.as_small_str().to_unsafe(), None)
{
Some(Branch {
db: branch,
patches: patches,
revpatches: revpatches,
apply_counter: counter,
name: name,
})
} else {
None
}
}
pub fn get_nodes<'a>(
&'a self,
branch: &Branch,
key: Key<PatchId>,
edge: Option<Edge>,
) -> Option<Edge> {
self.txn.get(&branch.db, key, edge)
}
pub fn iter_nodes<'a>(
&'a self,
branch: &'a Branch,
key: Option<(Key<PatchId>, Option<Edge>)>,
) -> NodesIterator<'a, U> {
NodesIterator(self.txn.iter(&branch.db, key.map(|(k, v)| (k, v))))
}
pub fn iter_adjacent<'a>(
&'a self,
branch: &'a Branch,
key: Key<PatchId>,
min_flag: EdgeFlags,
max_flag: EdgeFlags,
) -> AdjIterator<'a, U> {
let edge = Edge::zero(min_flag);
AdjIterator {
it: self.iter_nodes(branch, Some((key, Some(edge)))),
key,
max_flag: max_flag,
}
}
pub fn iter_parents<'a>(
&'a self,
branch: &'a Branch,
key: Key<PatchId>,
flag: EdgeFlags,
) -> AdjIterator<'a, U> {
let edge = Edge::zero(flag | EdgeFlags::PARENT_EDGE);
AdjIterator {
it: self.iter_nodes(branch, Some((key, Some(edge)))),
key,
max_flag: flag | EdgeFlags::PARENT_EDGE | EdgeFlags::PSEUDO_EDGE,
}
}
pub fn iter_folder_children<'a>(
&'a self,
branch: &'a Branch,
key: Key<PatchId>,
flag: EdgeFlags,
) -> AdjIterator<'a, U> {
let edge = Edge::zero(flag | EdgeFlags::FOLDER_EDGE);
AdjIterator {
it: self.iter_nodes(branch, Some((key, Some(edge)))),
key,
max_flag: flag
| EdgeFlags::FOLDER_EDGE
| EdgeFlags::PSEUDO_EDGE
| EdgeFlags::EPSILON_EDGE,
}
}
pub fn iter_branches<'a>(&'a self, key: Option<&SmallStr>) -> BranchIterator<'a, U> {
BranchIterator(
self.txn
.iter(&self.dbs.branches, key.map(|k| (k.to_unsafe(), None))),
)
}
pub fn iter_partials<'a>(&'a self, branch: &str) -> PartialsIterator<'a, U> {
let key = SmallString::from_str(branch);
PartialsIterator(self.txn.iter(
&self.dbs.partials,
Some((key.as_small_str().to_unsafe(), None)),
))
}
pub fn iter_patches<'a>(
&'a self,
branch: &'a Branch,
key: Option<PatchId>,
) -> PatchesIterator<'a, U> {
PatchesIterator(self.txn.iter(&branch.patches, key.map(|k| (k, None))))
}
pub fn rev_iter_applied<'a>(
&'a self,
branch: &'a Branch,
key: Option<ApplyTimestamp>,
) -> RevAppliedIterator<'a, U> {
RevAppliedIterator(
self.txn
.rev_iter(&branch.revpatches, key.map(|k| (k, None))),
)
}
pub fn iter_applied<'a>(
&'a self,
branch: &'a Branch,
key: Option<ApplyTimestamp>,
) -> AppliedIterator<'a, U> {
AppliedIterator(self.txn.iter(&branch.revpatches, key.map(|k| (k, None))))
}
pub fn iter_tree<'a>(&'a self, key: Option<(&FileId, Option<Inode>)>) -> TreeIterator<'a, U> {
debug!("iter_tree: {:?}", key);
TreeIterator(
self.txn
.iter(&self.dbs.tree, key.map(|(k, v)| (k.to_unsafe(), v))),
)
}
pub fn iter_revtree<'a>(
&'a self,
key: Option<(Inode, Option<&FileId>)>,
) -> RevtreeIterator<'a, U> {
RevtreeIterator(self.txn.iter(
&self.dbs.revtree,
key.map(|(k, v)| (k, v.map(|v| v.to_unsafe()))),
))
}
pub fn iter_inodes<'a>(
&'a self,
key: Option<(Inode, Option<FileHeader>)>,
) -> InodesIterator<'a, U> {
InodesIterator(self.txn.iter(&self.dbs.inodes, key))
}
pub fn iter_external<'a>(
&'a self,
key: Option<(PatchId, Option<HashRef>)>,
) -> ExternalIterator<'a, U> {
ExternalIterator(self.txn.iter(
&self.dbs.external,
key.map(|(k, v)| (k, v.map(|v| v.to_unsafe()))),
))
}
pub fn iter_internal<'a>(
&'a self,
key: Option<(HashRef, Option<PatchId>)>,
) -> InternalIterator<'a, U> {
InternalIterator(
self.txn
.iter(&self.dbs.internal, key.map(|(k, v)| (k.to_unsafe(), v))),
)
}
pub fn iter_revdep<'a>(
&'a self,
key: Option<(PatchId, Option<PatchId>)>,
) -> RevdepIterator<'a, U> {
RevdepIterator(self.txn.iter(&self.dbs.revdep, key))
}
pub fn iter_dep<'a>(&'a self, key: Option<(PatchId, Option<PatchId>)>) -> DepIterator<'a, U> {
DepIterator(self.txn.iter(&self.dbs.dep, key))
}
pub fn iter_contents<'a>(&'a self, key: Option<Key<PatchId>>) -> ContentsIterator<'a, U> {
ContentsIterator(
&self.txn,
self.txn.iter(&self.dbs.contents, key.map(|k| (k, None))),
)
}
pub fn iter_cemetery<'a>(&'a self, key: Key<PatchId>, edge: Edge) -> CemeteryIterator<'a, U> {
CemeteryIterator(self.txn.iter(&self.dbs.cemetery, Some(((key, edge), None))))
}
pub fn iter_touched<'a>(&'a self, key: Key<PatchId>) -> TouchedIterator<'a, U> {
TouchedIterator(self.txn.iter(&self.dbs.touched_files, Some((key, None))))
}
pub fn get_touched<'a>(&'a self, key: Key<PatchId>, patch: PatchId) -> bool {
self.txn
.get(&self.dbs.touched_files, key, Some(patch))
.is_some()
}
pub fn get_tree<'a>(&'a self, key: &FileId) -> Option<Inode> {
self.txn.get(&self.dbs.tree, key.to_unsafe(), None)
}
pub fn get_revtree<'a>(&'a self, key: Inode) -> Option<FileId<'a>> {
self.txn
.get(&self.dbs.revtree, key, None)
.map(|e| unsafe { FileId::from_unsafe(e) })
}
pub fn get_inodes<'a>(&'a self, key: Inode) -> Option<FileHeader> {
self.txn.get(&self.dbs.inodes, key, None)
}
pub fn get_revinodes(&self, key: Key<PatchId>) -> Option<Inode> {
self.txn.get(&self.dbs.revinodes, key, None)
}
pub fn get_contents<'a>(&'a self, key: Key<PatchId>) -> Option<Value<'a, U>> {
if let Some(e) = self.txn.get(&self.dbs.contents, key, None) {
unsafe { Some(Value::from_unsafe(&e, &self.txn)) }
} else {
None
}
}
pub fn get_internal(&self, key: HashRef) -> Option<PatchId> {
match key {
HashRef::None => Some(ROOT_PATCH_ID),
h => self.txn.get(&self.dbs.internal, h.to_unsafe(), None),
}
}
pub fn get_external<'a>(&'a self, key: PatchId) -> Option<HashRef<'a>> {
self.txn
.get(&self.dbs.external, key, None)
.map(|e| unsafe { HashRef::from_unsafe(e) })
}
pub fn get_patch(&self, patch_set: &PatchSet, patchid: PatchId) -> Option<ApplyTimestamp> {
self.txn.get(patch_set, patchid, None)
}
pub fn get_revdep(&self, patch: PatchId, dep: Option<PatchId>) -> Option<PatchId> {
self.txn.get(&self.dbs.revdep, patch, dep)
}
pub fn get_dep(&self, patch: PatchId, dep: Option<PatchId>) -> Option<PatchId> {
self.txn.get(&self.dbs.dep, patch, dep)
}
pub fn debug<W>(&self, branch_name: &str, w: &mut W, exclude_parents: bool)
where
W: std::io::Write,
{
debug!("debugging branch {:?}", branch_name);
let mut styles = Vec::with_capacity(16);
for i in 0..32 {
let flag = EdgeFlags::from_bits(i as u8).unwrap();
styles.push(
("color=").to_string()
+ ["red", "blue", "orange", "green", "black"][(i >> 1) & 3]
+ if flag.contains(EdgeFlags::DELETED_EDGE) {
", style=dashed"
} else {
""
}
+ if flag.contains(EdgeFlags::PSEUDO_EDGE) {
", style=dotted"
} else {
""
},
)
}
w.write(b"digraph{\n").unwrap();
let branch = self.get_branch(branch_name).unwrap();
let mut cur: Key<PatchId> = ROOT_KEY.clone();
for (k, v) in self.iter_nodes(&branch, None) {
if k != cur {
let cont = if let Some(cont) = self.get_contents(k) {
let cont = cont.into_cow();
let cont = &cont[..std::cmp::min(50, cont.len())];
format!(
"{:?}",
match std::str::from_utf8(cont) {
Ok(x) => x.to_string(),
Err(_) => hex::encode(cont),
}
)
} else {
"\"\"".to_string()
};
let cont = &cont[1..(cont.len() - 1)];
write!(
w,
"n_{}[label=\"{}.{}: {}\"];\n",
k.to_hex(),
k.patch.to_base58(),
k.line.to_hex(),
cont.replace("\n", "")
)
.unwrap();
cur = k.clone();
}
debug!("debug: {:?}", v);
let flag = v.flag.bits();
if !(exclude_parents && v.flag.contains(EdgeFlags::PARENT_EDGE)) {
write!(
w,
"n_{}->n_{}[{},label=\"{} {}\"];\n",
k.to_hex(),
&v.dest.to_hex(),
styles[(flag & 0xff) as usize],
flag,
v.introduced_by.to_base58()
)
.unwrap();
}
}
w.write(b"}\n").unwrap();
}
pub fn debug_folders<W>(&self, branch_name: &str, w: &mut W)
where
W: std::io::Write,
{
debug!("debugging branch {:?}", branch_name);
let mut styles = Vec::with_capacity(16);
for i in 0..32 {
let flag = EdgeFlags::from_bits(i as u8).unwrap();
styles.push(
("color=").to_string()
+ ["red", "blue", "orange", "green", "black"][(i >> 1) & 3]
+ if flag.contains(EdgeFlags::DELETED_EDGE) {
", style=dashed"
} else {
""
}
+ if flag.contains(EdgeFlags::PSEUDO_EDGE) {
", style=dotted"
} else {
""
},
)
}
w.write(b"digraph{\n").unwrap();
let branch = self.get_branch(branch_name).unwrap();
let mut nodes = vec![ROOT_KEY];
while let Some(k) = nodes.pop() {
let cont = if let Some(cont) = self.get_contents(k) {
let cont = cont.into_cow();
let cont = &cont[..std::cmp::min(50, cont.len())];
if cont.len() > 2 {
let (a, b) = cont.split_at(2);
let cont = format!("{:?}", std::str::from_utf8(b).unwrap());
let cont = &cont[1..(cont.len() - 1)];
format!("{} {}", hex::encode(a), cont)
} else {
format!("{}", hex::encode(cont))
}
} else {
"".to_string()
};
write!(
w,
"n_{}[label=\"{}.{}: {}\"];\n",
k.to_hex(),
k.patch.to_base58(),
k.line.to_hex(),
cont.replace("\n", "")
)
.unwrap();
for child in self.iter_adjacent(&branch, k, EdgeFlags::empty(), EdgeFlags::all()) {
let flag = child.flag.bits();
write!(
w,
"n_{}->n_{}[{},label=\"{} {}\"];\n",
k.to_hex(),
&child.dest.to_hex(),
styles[(flag & 0xff) as usize],
flag,
child.introduced_by.to_base58()
)
.unwrap();
if child.flag.contains(EdgeFlags::FOLDER_EDGE)
&& !child.flag.contains(EdgeFlags::PARENT_EDGE)
{
nodes.push(child.dest)
}
}
}
w.write(b"}\n").unwrap();
}
pub fn is_connected(&self, branch: &Branch, a: Key<PatchId>, b: Key<PatchId>) -> bool {
self.test_edge(
branch,
a,
b,
EdgeFlags::empty(),
EdgeFlags::PSEUDO_EDGE | EdgeFlags::FOLDER_EDGE,
)
}
pub fn test_edge(
&self,
branch: &Branch,
a: Key<PatchId>,
b: Key<PatchId>,
min: EdgeFlags,
max: EdgeFlags,
) -> bool {
debug!("is_connected {:?} {:?}", a, b);
let mut edge = Edge::zero(min);
edge.dest = b;
self.iter_nodes(&branch, Some((a, Some(edge))))
.take_while(|&(k, v)| k == a && v.dest == b && v.flag <= max)
.next()
.is_some()
}
}
impl<'env, R: rand::Rng> MutTxn<'env, R> {
pub fn drop_branch(&mut self, branch: &str) -> Result<bool> {
let name = SmallString::from_str(branch);
Ok(self.txn.del(
&mut self.rng,
&mut self.dbs.branches,
name.as_small_str().to_unsafe(),
None,
)?)
}
pub fn put_edge_one_dir(
&mut self,
branch: &mut Branch,
key: Key<PatchId>,
edge: Edge,
) -> Result<bool> {
debug!("put_nodes: {:?} {:?}", key, edge);
Ok(self.txn.put(&mut self.rng, &mut branch.db, key, edge)?)
}
pub fn put_edge_both_dirs(
&mut self,
branch: &mut Branch,
mut key: Key<PatchId>,
mut edge: Edge,
) -> Result<bool> {
self.put_edge_one_dir(branch, key, edge)?;
std::mem::swap(&mut key, &mut edge.dest);
edge.flag.toggle(EdgeFlags::PARENT_EDGE);
self.put_edge_one_dir(branch, key, edge)
}
pub fn del_edge_one_dir(
&mut self,
branch: &mut Branch,
key: Key<PatchId>,
edge: Edge,
) -> Result<bool> {
Ok(self
.txn
.del(&mut self.rng, &mut branch.db, key, Some(edge))?)
}
pub fn del_edge_both_dirs(
&mut self,
branch: &mut Branch,
mut key: Key<PatchId>,
mut edge: Edge,
) -> Result<bool> {
self.del_edge_one_dir(branch, key, edge)?;
std::mem::swap(&mut key, &mut edge.dest);
edge.flag.toggle(EdgeFlags::PARENT_EDGE);
self.del_edge_one_dir(branch, key, edge)
}
pub fn put_tree(&mut self, key: &FileId, edge: Inode) -> Result<bool> {
Ok(self
.txn
.put(&mut self.rng, &mut self.dbs.tree, key.to_unsafe(), edge)?)
}
pub fn del_tree(&mut self, key: &FileId, edge: Option<Inode>) -> Result<bool> {
Ok(self
.txn
.del(&mut self.rng, &mut self.dbs.tree, key.to_unsafe(), edge)?)
}
pub fn put_revtree(&mut self, key: Inode, value: &FileId) -> Result<bool> {
Ok(self
.txn
.put(&mut self.rng, &mut self.dbs.revtree, key, value.to_unsafe())?)
}
pub fn del_revtree(&mut self, key: Inode, value: Option<&FileId>) -> Result<bool> {
Ok(self.txn.del(
&mut self.rng,
&mut self.dbs.revtree,
key,
value.map(|e| e.to_unsafe()),
)?)
}
pub fn del_inodes(&mut self, key: Inode, value: Option<FileHeader>) -> Result<bool> {
Ok(self
.txn
.del(&mut self.rng, &mut self.dbs.inodes, key, value)?)
}
pub fn replace_inodes(&mut self, key: Inode, value: FileHeader) -> Result<bool> {
self.txn
.del(&mut self.rng, &mut self.dbs.inodes, key, None)?;
Ok(self
.txn
.put(&mut self.rng, &mut self.dbs.inodes, key, value)?)
}
pub fn replace_revinodes(&mut self, key: Key<PatchId>, value: Inode) -> Result<bool> {
self.txn
.del(&mut self.rng, &mut self.dbs.revinodes, key, None)?;
Ok(self
.txn
.put(&mut self.rng, &mut self.dbs.revinodes, key, value)?)
}
pub fn del_revinodes(&mut self, key: Key<PatchId>, value: Option<Inode>) -> Result<bool> {
Ok(self
.txn
.del(&mut self.rng, &mut self.dbs.revinodes, key, value)?)
}
pub fn put_contents(&mut self, key: Key<PatchId>, value: UnsafeValue) -> Result<bool> {
Ok(self
.txn
.put(&mut self.rng, &mut self.dbs.contents, key, value)?)
}
pub fn del_contents(&mut self, key: Key<PatchId>, value: Option<UnsafeValue>) -> Result<bool> {
Ok(self
.txn
.del(&mut self.rng, &mut self.dbs.contents, key, value)?)
}
pub fn put_internal(&mut self, key: HashRef, value: PatchId) -> Result<bool> {
Ok(self.txn.put(
&mut self.rng,
&mut self.dbs.internal,
key.to_unsafe(),
value,
)?)
}
pub fn del_internal(&mut self, key: HashRef) -> Result<bool> {
Ok(self
.txn
.del(&mut self.rng, &mut self.dbs.internal, key.to_unsafe(), None)?)
}
pub fn put_external(&mut self, key: PatchId, value: HashRef) -> Result<bool> {
Ok(self.txn.put(
&mut self.rng,
&mut self.dbs.external,
key,
value.to_unsafe(),
)?)
}
pub fn del_external(&mut self, key: PatchId) -> Result<bool> {
Ok(self
.txn
.del(&mut self.rng, &mut self.dbs.external, key, None)?)
}
pub fn put_patches(
&mut self,
branch: &mut PatchSet,
value: PatchId,
time: ApplyTimestamp,
) -> Result<bool> {
Ok(self.txn.put(&mut self.rng, branch, value, time)?)
}
pub fn del_patches(&mut self, branch: &mut PatchSet, value: PatchId) -> Result<bool> {
Ok(self.txn.del(&mut self.rng, branch, value, None)?)
}
pub fn put_revpatches(
&mut self,
branch: &mut RevPatchSet,
time: ApplyTimestamp,
value: PatchId,
) -> Result<bool> {
Ok(self.txn.put(&mut self.rng, branch, time, value)?)
}
pub fn del_revpatches(
&mut self,
revbranch: &mut RevPatchSet,
timestamp: ApplyTimestamp,
value: PatchId,
) -> Result<bool> {
Ok(self
.txn
.del(&mut self.rng, revbranch, timestamp, Some(value))?)
}
pub fn put_revdep(&mut self, patch: PatchId, revdep: PatchId) -> Result<bool> {
Ok(self
.txn
.put(&mut self.rng, &mut self.dbs.revdep, patch, revdep)?)
}
pub fn put_dep(&mut self, patch: PatchId, dep: PatchId) -> Result<bool> {
Ok(self.txn.put(&mut self.rng, &mut self.dbs.dep, patch, dep)?)
}
pub fn del_revdep(&mut self, patch: PatchId, revdep: Option<PatchId>) -> Result<bool> {
Ok(self
.txn
.del(&mut self.rng, &mut self.dbs.revdep, patch, revdep)?)
}
pub fn del_dep(&mut self, patch: PatchId, dep: Option<PatchId>) -> Result<bool> {
Ok(self.txn.del(&mut self.rng, &mut self.dbs.dep, patch, dep)?)
}
pub fn put_cemetery(&mut self, key: Key<PatchId>, edge: Edge, patch: PatchId) -> Result<bool> {
Ok(self
.txn
.put(&mut self.rng, &mut self.dbs.cemetery, (key, edge), patch)?)
}
pub fn del_cemetery(&mut self, key: Key<PatchId>, edge: Edge, patch: PatchId) -> Result<bool> {
Ok(self.txn.del(
&mut self.rng,
&mut self.dbs.cemetery,
(key, edge),
Some(patch),
)?)
}
pub fn put_touched_file(&mut self, file: Key<PatchId>, patch: PatchId) -> Result<bool> {
Ok(self
.txn
.put(&mut self.rng, &mut self.dbs.touched_files, file, patch)?)
}
pub fn del_touched_file(&mut self, file: Key<PatchId>, patch: PatchId) -> Result<bool> {
Ok(self.txn.del(
&mut self.rng,
&mut self.dbs.touched_files,
file,
Some(patch),
)?)
}
pub fn put_partials(&mut self, name: &str, path: Key<PatchId>) -> Result<bool> {
let name = small_string::SmallString::from_str(name);
Ok(self.txn.put(
&mut self.rng,
&mut self.dbs.partials,
name.as_small_str().to_unsafe(),
path,
)?)
}
pub fn del_partials(&mut self, name: &str) -> Result<bool> {
let name = small_string::SmallString::from_str(name);
let mut deleted = false;
while self.txn.del(
&mut self.rng,
&mut self.dbs.partials,
name.as_small_str().to_unsafe(),
None,
)? {
deleted = true
}
Ok(deleted)
}
pub fn alloc_value(&mut self, slice: &[u8]) -> Result<UnsafeValue> {
Ok(UnsafeValue::alloc_if_needed(&mut self.txn, slice)?)
}
}