Tags

[?]
Mar 10, 2021, 4:56 PM
QL6K2ZM35B3NIXEMMCJWUSFXOBQHAGXRDMO7ID5DCKTJH4QJVY7QC

Dependencies

  • [2] ZBNKSYA6 Fixing a bus error when starting a transaction on a full disk
  • [3] 7S4YD633 Change in semantic of the new Sanakirja compared to the previous one (get returns Some(…) even if the key is not found)
  • [4] CCLLB7OI Upgrading to Sanakirja 0.15 + version bump
  • [5] BXD3IQYN Fixing --features git
  • [6] JL4WKA5P Implement the Sanakirja concurrency model in a cross-process way
  • [7] SNZ3OAMC use native external subcommand support instead of hand-rolled one
  • [8] YN63NUZO Sanakirja 1.0
  • [9] IIV3EL2X Cleanup, formatting, and fixing the Git feature
  • [10] I52XSRUH Massive cleanup, and simplification
  • [11] H23LO7U7 a few more clippy lints addressed
  • [12] SXEYMYF7 Fixing the bad changes in history (unfortunately, by rebooting).
  • [13] I24UEJQL Various post-fire fixes
  • [14] GHO6DWPI Refactoring iterators
  • [15] 7ZFRYVVQ Cargo.nix and formatting
  • [16] VO5OQW4W Removing anyhow in libpijul
  • [*] TZ42DX3B Properly dropping a channel

Change contents

  • replacement in pijul/src/main.rs at line 125
    [3.9020][3.9020:9037]()
    // Tag(Tag),
    [3.9020]
    [3.25]
    Tag(Tag),
  • replacement in pijul/src/main.rs at line 213
    [3.5590][3.9038:9090]()
    // SubCommand::Tag(tag) => tag.run().await,
    [3.5590]
    [3.600]
    SubCommand::Tag(tag) => tag.run().await,
  • file addition: tag.rs (----------)
    [3.93386]
    use std::io::Write;
    use std::path::PathBuf;
    use std::sync::{Arc, RwLock};
    use crate::repository::Repository;
    use anyhow::bail;
    use clap::Clap;
    use libpijul::change::ChangeHeader;
    use libpijul::{Base32, ChannelMutTxnT, ChannelTxnT, MutTxnT, TxnT, TxnTExt};
    #[derive(Clap, Debug)]
    pub struct Tag {
    /// Set the repository where this command should run. Defaults to
    /// the first ancestor of the current directory that contains a
    /// `.pijul` directory.
    #[clap(long = "repository")]
    repo_path: Option<PathBuf>,
    #[clap(subcommand)]
    subcmd: SubCommand,
    }
    #[derive(Clap, Debug)]
    pub enum SubCommand {
    /// Create a tag.
    #[clap(name = "create")]
    Create {
    #[clap(short = 'm', long = "message")]
    message: Option<String>,
    /// Set the author field
    #[clap(long = "author")]
    author: Option<String>,
    /// Record the change in this channel instead of the current channel
    #[clap(long = "channel")]
    channel: Option<String>,
    #[clap(long = "timestamp")]
    timestamp: Option<i64>,
    },
    /// Restore a tag into a new channel.
    #[clap(name = "checkout")]
    Checkout {
    tag: String,
    /// Optional new channel name. If not given, the base32
    /// representation of the tag hash is used.
    #[clap(long = "to-channel")]
    to_channel: Option<String>,
    },
    }
    impl Tag {
    pub async fn run(self) -> Result<(), anyhow::Error> {
    let mut stdout = std::io::stdout();
    let mut repo = Repository::find_root(self.repo_path).await?;
    match self.subcmd {
    SubCommand::Create {
    message,
    author,
    channel,
    timestamp,
    } => {
    let channel_name = repo
    .config
    .get_current_channel(channel.as_deref())
    .0
    .to_string();
    try_record(&mut repo, &channel_name)?;
    let mut txn = repo.pristine.mut_txn_begin()?;
    let channel = txn.load_channel(&channel_name)?.unwrap();
    let last_t = if let Some(n) = txn.reverse_log(&*channel.read()?, None)?.next() {
    n?.0.into()
    } else {
    bail!("Channel {} is empty", channel_name);
    };
    log::debug!("last_t = {:?}", last_t);
    if txn.get_tags(&channel.read()?.tags, &last_t)?.is_some() {
    bail!("Current state is already tagged")
    }
    let mut tag_path = repo.path.join(libpijul::DOT_DIR);
    tag_path.push("tags");
    std::fs::create_dir_all(&tag_path)?;
    let mut temp_path = tag_path.clone();
    temp_path.push("tag");
    let mut w = std::fs::File::create(&temp_path)?;
    let header = header(author.as_deref(), message, timestamp)?;
    let h = libpijul::tag::from_channel(&txn, &channel_name, &header, &mut w)?;
    libpijul::changestore::filesystem::push_filename(&mut tag_path, &h);
    std::fs::create_dir_all(tag_path.parent().unwrap())?;
    std::fs::rename(&temp_path, &tag_path)?;
    txn.put_tags(&mut *channel.write()?, last_t.into(), &h)?;
    txn.commit()?;
    writeln!(stdout, "{}", h.to_base32())?;
    }
    SubCommand::Checkout { tag, to_channel } => {
    let h = if let Some(h) = libpijul::Hash::from_base32(tag.as_bytes()) {
    h
    } else {
    bail!("Invalid tag {:?}", tag)
    };
    let channel_name = if let Some(ref channel) = to_channel {
    channel.as_str()
    } else {
    tag.as_str()
    };
    let mut txn = repo.pristine.mut_txn_begin()?;
    if txn.load_channel(channel_name)?.is_some() {
    bail!("Channel {:?} already exists", channel_name)
    }
    let mut tag_path = repo.path.join(libpijul::DOT_DIR);
    tag_path.push("tags");
    libpijul::changestore::filesystem::push_filename(&mut tag_path, &h);
    let f = libpijul::tag::OpenTagFile::open(&tag)?;
    libpijul::tag::restore_channel(f, &mut txn, &channel_name)?;
    txn.commit()?;
    }
    }
    Ok(())
    }
    }
    fn header(
    author: Option<&str>,
    message: Option<String>,
    timestamp: Option<i64>,
    ) -> Result<ChangeHeader, anyhow::Error> {
    let authors = if let Some(a) = author {
    vec![libpijul::change::Author {
    name: a.to_string(),
    full_name: None,
    email: None,
    }]
    } else if let Ok(global) = crate::config::Global::load() {
    vec![global.author]
    } else {
    Vec::new()
    };
    let header = ChangeHeader {
    message: message.clone().unwrap_or_else(String::new),
    authors,
    description: None,
    timestamp: if let Some(t) = timestamp {
    chrono::DateTime::from_utc(chrono::NaiveDateTime::from_timestamp(t, 0), chrono::Utc)
    } else {
    chrono::Utc::now()
    },
    };
    let toml = toml::to_string_pretty(&header)?;
    loop {
    let bytes = edit::edit_bytes(toml.as_bytes())?;
    if let Ok(header) = toml::from_slice(&bytes) {
    return Ok(header);
    }
    }
    }
    fn try_record(repo: &mut Repository, channel: &str) -> Result<(), anyhow::Error> {
    let txn = repo.pristine.mut_txn_begin()?;
    if let Some(channel) = txn.load_channel(channel)? {
    let mut state = libpijul::RecordBuilder::new();
    state.record(
    Arc::new(RwLock::new(txn)),
    libpijul::Algorithm::default(),
    channel,
    repo.working_copy.clone(),
    &repo.changes,
    "",
    num_cpus::get(),
    )?;
    let rec = state.finish();
    if !rec.actions.is_empty() {
    bail!("Cannot change channel, as there are unrecorded changes.")
    }
    } else {
    bail!("Channel not found: {}", channel)
    }
    Ok(())
    }
  • replacement in pijul/src/commands/mod.rs at line 54
    [3.134253][3.59:90]()
    // mod tag;
    // pub use tag::*;
    [3.134253]
    [3.90]
    mod tag;
    pub use tag::*;
  • file addition: tag.rs (----------)
    [3.198146]
    use crate::pristine::sanakirja::{Channel, MutTxn, SanakirjaError, P, UP};
    use crate::pristine::*;
    use crate::HashSet;
    use crate::TxnT;
    use log::*;
    use serde_derive::*;
    use std::io::Read;
    use std::path::Path;
    use std::sync::{Arc, RwLock};
    #[derive(Debug, Serialize, Deserialize, Default)]
    struct FileHeader {
    version: u64,
    header: u64,
    channel: u64,
    unhashed: u64,
    total: u64,
    offsets: DbOffsets,
    state: Merkle,
    }
    #[derive(Debug, Serialize, Deserialize, Default)]
    struct DbOffsets {
    internal: u64,
    external: u64,
    graph: u64,
    changes: u64,
    revchanges: u64,
    states: u64,
    tags: u64,
    apply_counter: u64,
    size: u64,
    }
    pub struct OpenTagFile {
    header: FileHeader,
    file: std::fs::File,
    }
    #[derive(Debug, Error)]
    pub enum TagError {
    #[error("Version mismatch")]
    VersionMismatch,
    #[error(transparent)]
    Io(#[from] std::io::Error),
    #[error(transparent)]
    Bincode(#[from] bincode::Error),
    #[error(transparent)]
    Zstd(#[from] zstd_seekable::Error),
    #[error(transparent)]
    Txn(SanakirjaError),
    #[error("Synchronisation error")]
    Sync,
    }
    impl From<TxnErr<SanakirjaError>> for TagError {
    fn from(e: TxnErr<SanakirjaError>) -> Self {
    TagError::Txn(e.0)
    }
    }
    impl OpenTagFile {
    pub fn open<P: AsRef<Path>>(p: P) -> Result<Self, TagError> {
    let mut file = std::fs::File::open(p)?;
    let mut off = [0u8; std::mem::size_of::<FileHeader>() as usize];
    file.read_exact(&mut off)?;
    let header = bincode::deserialize(&off)?;
    Ok(OpenTagFile { header, file })
    }
    }
    pub const VERSION: u64 = 5;
    const BLOCK_SIZE: usize = 4096;
    pub fn restore_channel(
    mut tag: OpenTagFile,
    txn: &mut MutTxn<()>,
    name: &str,
    ) -> Result<ChannelRef<MutTxn<()>>, TagError> {
    use std::io::{Seek, SeekFrom};
    tag.file.seek(SeekFrom::Start(tag.header.channel))?;
    let mut comp = vec![0; (tag.header.unhashed - tag.header.channel) as usize];
    debug!("tag header {:?}", tag.header);
    tag.file.read_exact(&mut comp)?;
    debug!("{:?} {:?}", &comp[..20], comp.len());
    debug!("{:?}", &comp[comp.len() - 20..]);
    let mut buf = vec![0; tag.header.offsets.size as usize];
    zstd_seekable::Seekable::init_buf(&comp)?
    .decompress(&mut buf, 0)
    .unwrap();
    let filetxn = Txn::from_slice(&mut buf);
    let external: ::sanakirja::btree::Db_<ChangeId, SerializedHash, UP<ChangeId, SerializedHash>> =
    ::sanakirja::btree::Db_::from_page(tag.header.offsets.external);
    debug!("restoring graph");
    let graph = restore(
    &filetxn,
    txn,
    tag.header.offsets.graph,
    |file_txn, txn, k: &Vertex<ChangeId>, v: &SerializedEdge| {
    let k = if k.change.is_root() {
    *k
    } else {
    debug!("btree get: {:?}", k.change);
    let (kc, h) =
    ::sanakirja::btree::get(file_txn, &external, &k.change, None)?.unwrap();
    assert_eq!(k.change, *kc);
    Vertex {
    change: crate::pristine::make_changeid(txn, &h.into())?,
    ..*k
    }
    };
    let dest = v.dest();
    let dest = {
    if dest.change.is_root() {
    dest
    } else {
    let (vd, change) =
    ::sanakirja::btree::get(file_txn, &external, &dest.change, None)?.unwrap();
    assert_eq!(v.dest().change, *vd);
    Position {
    change: crate::pristine::make_changeid(txn, &change.into())?,
    ..v.dest()
    }
    }
    };
    let introduced_by = v.introduced_by();
    let introduced_by = if introduced_by.is_root() {
    introduced_by
    } else {
    let (vi, change) =
    ::sanakirja::btree::get(file_txn, &external, &introduced_by, None)?.unwrap();
    assert_eq!(introduced_by, *vi);
    crate::pristine::make_changeid(txn, &change.into())?
    };
    let v = Edge {
    dest,
    introduced_by,
    ..v.into()
    };
    Ok((k, v.into()))
    },
    )?;
    debug!("restoring changes");
    let changes = restore(
    &filetxn,
    txn,
    tag.header.offsets.changes,
    |file_txn, txn, k: &ChangeId, v: &L64| {
    let (k_, h) = ::sanakirja::btree::get(file_txn, &external, k, None)?.unwrap();
    assert_eq!(k, k_);
    let k = crate::pristine::make_changeid(txn, &h.into())?;
    Ok((k, *v))
    },
    )?;
    debug!("restoring revchanges");
    let revchanges = restore(
    &filetxn,
    txn,
    tag.header.offsets.revchanges,
    |file_txn, txn, k: &L64, v: &Pair<ChangeId, SerializedMerkle>| {
    let (v0, h) = ::sanakirja::btree::get(file_txn, &external, &v.a, None)?.unwrap();
    assert_eq!(v.a, *v0);
    let v_ = crate::pristine::make_changeid(txn, &h.into())?;
    Ok((
    *k,
    Pair {
    a: v_,
    b: v.b.clone(),
    },
    ))
    },
    )?;
    debug!("restoring states");
    let states = restore(
    &filetxn,
    txn,
    tag.header.offsets.states,
    |_, _, k: &SerializedMerkle, v: &L64| Ok((k.clone(), *v)),
    )?;
    debug!("restoring states");
    let tags = restore(
    &filetxn,
    txn,
    tag.header.offsets.tags,
    |_, _, k: &L64, v: &SerializedHash| Ok((*k, *v)),
    )?;
    let name = crate::small_string::SmallString::from_str(name);
    let br = ChannelRef {
    r: Arc::new(RwLock::new(Channel {
    graph,
    changes,
    revchanges,
    states,
    tags,
    apply_counter: tag.header.offsets.apply_counter,
    name: name.clone(),
    last_modified: 0,
    })),
    };
    txn.open_channels.lock().unwrap().insert(name, br.clone());
    Ok(br)
    }
    struct Txn<'a> {
    data: *mut u8,
    marker: std::marker::PhantomData<&'a ()>,
    }
    impl<'a> Txn<'a> {
    fn from_slice(s: &'a mut [u8]) -> Self {
    Txn {
    data: s.as_mut_ptr(),
    marker: std::marker::PhantomData,
    }
    }
    }
    impl<'a> ::sanakirja::LoadPage for Txn<'a> {
    type Error = ::sanakirja::CRCError;
    fn load_page(&self, off: u64) -> Result<::sanakirja::CowPage, ::sanakirja::CRCError> {
    Ok(::sanakirja::CowPage {
    data: unsafe { self.data.add(off as usize) },
    offset: off,
    })
    }
    }
    fn restore<
    K: ::sanakirja::UnsizedStorable,
    V: ::sanakirja::UnsizedStorable,
    P: ::sanakirja::btree::BTreeMutPage<K, V>,
    F,
    >(
    file_txn: &Txn,
    txn: &mut crate::pristine::sanakirja::MutTxn<()>,
    pending: u64,
    f: F,
    ) -> Result<::sanakirja::btree::Db_<K, V, P>, TxnErr<SanakirjaError>>
    where
    F: Fn(
    &Txn,
    &mut crate::pristine::sanakirja::MutTxn<()>,
    &K,
    &V,
    ) -> Result<(K, V), TxnErr<SanakirjaError>>,
    {
    use ::sanakirja::AllocPage;
    let mut dict = HashSet::default();
    let page = txn.txn.alloc_page()?;
    let result = page.0.offset;
    let mut pending = vec![(pending, page)];
    while let Some((offset, mut new_page_)) = pending.pop() {
    debug!("{:?}", offset);
    let page = ::sanakirja::CowPage {
    data: unsafe { file_txn.data.offset(offset as isize) },
    offset,
    };
    let mut curs = P::cursor_first(&page);
    let mut new_curs = P::cursor_first(&new_page_.0);
    P::init(&mut new_page_);
    unsafe {
    P::set_left_child(
    &mut new_page_,
    &new_curs,
    P::left_child(page.as_page(), &curs),
    );
    }
    while let Some((k, v, r)) = P::next(&txn.txn, page.as_page(), &mut curs) {
    let (k, v) = f(file_txn, txn, k, v)?;
    let r = if r > 0 {
    assert!(dict.insert(r));
    let new_page = txn.txn.alloc_page()?;
    let off = new_page.0.offset;
    pending.push((r, new_page));
    off
    } else {
    0
    };
    unsafe { P::put_mut(&mut new_page_, &new_curs, &k, &v, r) }
    P::move_next(&mut new_curs);
    }
    }
    Ok(::sanakirja::btree::Db_::from_page(result))
    }
    pub fn from_channel<W: std::io::Write, T: ::sanakirja::LoadPage<Error = ::sanakirja::Error>>(
    txn: &crate::pristine::sanakirja::GenericTxn<T>,
    channel: &str,
    header: &crate::change::ChangeHeader,
    mut w: W,
    ) -> Result<Hash, TagError> {
    let out = Vec::with_capacity(1 << 16);
    let (out, offsets, state) = compress_channel(txn, channel, out)?;
    debug!("{:?} {:?}", &out[..20], out.len());
    debug!("{:?}", &out[out.len() - 20..]);
    let mut header_buf = Vec::with_capacity(1 << 10);
    bincode::serialize_into(&mut header_buf, header).unwrap();
    let mut off = FileHeader {
    version: VERSION,
    header: 0,
    channel: 0,
    unhashed: 0,
    total: 0,
    offsets,
    state,
    };
    off.header = bincode::serialized_size(&off)?;
    off.channel = off.header + header_buf.len() as u64;
    off.unhashed = off.channel + out.len() as u64;
    off.total = off.unhashed;
    let mut hasher = Hasher::default();
    let mut off_buf = Vec::with_capacity(off.header as usize);
    bincode::serialize_into(&mut off_buf, &off)?;
    debug!("off_buf = {:?}", off_buf.len());
    w.write_all(&off_buf)?;
    hasher.update(&off_buf);
    debug!("header_buf = {:?}", header_buf.len());
    w.write_all(&header_buf)?;
    hasher.update(&header_buf);
    debug!("out = {:?}", out.len());
    w.write_all(&out)?;
    hasher.update(&out);
    Ok(hasher.finish())
    }
    const LEVEL: usize = 10;
    const PIPE_LEN: usize = 10;
    fn compress_channel<
    W: std::io::Write + Send + 'static,
    T: ::sanakirja::LoadPage<Error = ::sanakirja::Error>,
    >(
    txn: &crate::pristine::sanakirja::GenericTxn<T>,
    channel: &str,
    mut to: W,
    ) -> Result<(W, DbOffsets, Merkle), TagError> {
    debug!("int = {:?}", txn.internal.db);
    let (sender, receiver) = std::sync::mpsc::sync_channel::<Vec<u8>>(PIPE_LEN);
    let (bsender, breceiver) = std::sync::mpsc::sync_channel::<Vec<u8>>(PIPE_LEN);
    for _ in 0..PIPE_LEN {
    bsender.send(vec![0; 4096]).map_err(|_| TagError::Sync)?;
    }
    let t = std::thread::spawn(move || -> Result<(W, usize), TagError> {
    let mut comp = zstd_seekable::SeekableCStream::new(LEVEL, BLOCK_SIZE).unwrap();
    let mut out = [0; BLOCK_SIZE];
    let mut n = 0;
    while let Ok(input) = receiver.recv() {
    n += BLOCK_SIZE;
    let mut input_off = 0;
    let mut output_off = 0;
    while input_off < BLOCK_SIZE as usize {
    let (a, b) = comp
    .compress(&mut out[output_off..], &input[input_off..])
    .unwrap();
    output_off += a;
    input_off += b;
    }
    to.write_all(&out[..output_off]).unwrap();
    bsender.send(input).map_err(|_| TagError::Sync)?;
    }
    while let Ok(n) = comp.end_stream(&mut out) {
    if n == 0 {
    break;
    }
    to.write_all(&out[..n])?;
    }
    Ok((to, n))
    });
    let channel = txn.load_channel(channel)?.unwrap();
    let channel = channel.read().unwrap();
    let mut new = 0;
    debug!("copying internal");
    let internal = copy::<SerializedHash, ChangeId, UP<SerializedHash, ChangeId>, _>(
    txn,
    txn.internal.db,
    &mut new,
    &sender,
    &breceiver,
    )?;
    debug!("copying external");
    let external = copy::<ChangeId, SerializedHash, UP<ChangeId, SerializedHash>, _>(
    txn,
    txn.external.db,
    &mut new,
    &sender,
    &breceiver,
    )?;
    debug!("copying graph");
    let graph = copy::<Vertex<ChangeId>, SerializedEdge, P<Vertex<ChangeId>, SerializedEdge>, _>(
    txn,
    channel.graph.db,
    &mut new,
    &sender,
    &breceiver,
    )?;
    debug!("copying changes");
    let changes = copy::<ChangeId, L64, P<ChangeId, L64>, _>(
    txn,
    channel.changes.db,
    &mut new,
    &sender,
    &breceiver,
    )?;
    debug!("copying revchanges");
    let revchanges = copy::<
    L64,
    Pair<ChangeId, SerializedMerkle>,
    UP<L64, Pair<ChangeId, SerializedMerkle>>,
    _,
    >(&txn, channel.revchanges.db, &mut new, &sender, &breceiver)?;
    debug!("copying states");
    let states = copy::<SerializedMerkle, L64, UP<SerializedMerkle, L64>, _>(
    txn,
    channel.states.db,
    &mut new,
    &sender,
    &breceiver,
    )?;
    let tags = copy::<L64, SerializedHash, UP<L64, SerializedHash>, _>(
    &txn,
    channel.states.db,
    &mut new,
    &sender,
    &breceiver,
    )?;
    std::mem::drop(sender);
    let (w, n) = t.join().unwrap()?;
    let state = crate::pristine::current_state(txn, &channel)?;
    Ok((
    w,
    DbOffsets {
    internal,
    external,
    graph,
    changes,
    revchanges,
    states,
    tags: tags,
    apply_counter: channel.apply_counter,
    size: n as u64,
    },
    state,
    ))
    }
    fn copy<
    K: ::sanakirja::UnsizedStorable,
    V: ::sanakirja::UnsizedStorable,
    P: ::sanakirja::btree::BTreeMutPage<K, V>,
    T: ::sanakirja::LoadPage<Error = ::sanakirja::Error>,
    >(
    txn: &crate::pristine::sanakirja::GenericTxn<T>,
    pending: u64,
    new_page: &mut u64,
    sender: &std::sync::mpsc::SyncSender<Vec<u8>>,
    buffers: &std::sync::mpsc::Receiver<Vec<u8>>,
    ) -> Result<u64, TagError> {
    let mut dict = HashSet::default();
    let result = *new_page;
    let mut pending = vec![(pending, *new_page)];
    *new_page += BLOCK_SIZE as u64;
    while let Some((old_page_off, new_page_)) = pending.pop() {
    let page = txn.txn.load_page(old_page_off).unwrap();
    let mut memory = buffers.recv().map_err(|_| TagError::Sync)?;
    let mut new_page_ = ::sanakirja::MutPage(::sanakirja::CowPage {
    data: memory.as_mut_ptr(),
    offset: new_page_,
    });
    P::init(&mut new_page_);
    let mut curs = P::cursor_first(&page);
    let mut new_curs = P::cursor_first(&new_page_.0);
    unsafe {
    P::set_left_child(
    &mut new_page_,
    &new_curs,
    P::left_child(page.as_page(), &curs),
    );
    }
    while let Some((k, v, r)) = P::next(&txn.txn, page.as_page(), &mut curs) {
    let r = if r > 0 {
    assert!(dict.insert(r));
    let new = *new_page;
    *new_page += BLOCK_SIZE as u64;
    pending.push((r, new));
    unsafe {
    P::set_left_child(&mut new_page_, &curs, new);
    }
    new
    } else {
    0
    };
    debug!("put {:?} {:?}", k, v);
    unsafe { P::put_mut(&mut new_page_, &new_curs, k, v, r) }
    P::move_next(&mut new_curs);
    }
    sender.send(memory).unwrap();
    }
    Ok(result)
    }
  • replacement in libpijul/src/pristine/sanakirja.rs at line 13
    [3.57013][3.57013:57053]()
    type P<K, V> = btree::page::Page<K, V>;
    [3.57013]
    [3.57053]
    pub(crate) type P<K, V> = btree::page::Page<K, V>;
  • replacement in libpijul/src/pristine/sanakirja.rs at line 15
    [3.57086][3.57086:57135]()
    type UP<K, V> = btree::page_unsized::Page<K, V>;
    [3.57086]
    [3.57135]
    pub(crate) type UP<K, V> = btree::page_unsized::Page<K, V>;
  • edit in libpijul/src/pristine/sanakirja.rs at line 30
    [18.88]
    [3.38798]
    #[error("Pristine version mismatch. Cloning over the network can fix this.")]
    Version,
  • edit in libpijul/src/pristine/sanakirja.rs at line 100
    [3.30332]
    [3.532545]
    Version,
  • edit in libpijul/src/pristine/sanakirja.rs at line 115
    [3.532726]
    [3.532726]
    const VERSION: L64 = L64(1u64.to_le());
  • edit in libpijul/src/pristine/sanakirja.rs at line 121
    [3.532869]
    [3.57378]
    if L64(txn.root(Root::Version as usize)) != VERSION {
    return Err(SanakirjaError::Version);
    }
  • replacement in libpijul/src/pristine/sanakirja.rs at line 152
    [3.534125][2.1200:1263]()
    pub fn mut_txn_begin(&self) -> Result<MutTxn<()>, Error> {
    [3.534125]
    [3.534173]
    pub fn mut_txn_begin(&self) -> Result<MutTxn<()>, SanakirjaError> {
  • edit in libpijul/src/pristine/sanakirja.rs at line 154
    [3.534255]
    [2.1264]
    if let Some(version) = txn.root(Root::Version as usize) {
    if L64(version) != VERSION {
    return Err(SanakirjaError::Version.into());
    }
    } else {
    txn.set_root(Root::Version as usize, VERSION.0);
    }
  • replacement in libpijul/src/pristine/sanakirja.rs at line 250
    [3.30387][3.60152:60200]()
    pub internal: Db<SerializedHash, ChangeId>,
    [3.30387]
    [3.30425]
    pub internal: UDb<SerializedHash, ChangeId>,
  • replacement in libpijul/src/pristine/sanakirja.rs at line 252
    [3.30444][3.60201:60249]()
    pub external: Db<ChangeId, SerializedHash>,
    [3.30444]
    [3.537121]
    pub external: UDb<ChangeId, SerializedHash>,
  • replacement in libpijul/src/pristine/sanakirja.rs at line 266
    [3.60363][3.60363:60396]()
    channels: UDb<SmallStr, T6>,
    [3.60363]
    [3.60396]
    channels: UDb<SmallStr, T8>,
  • edit in libpijul/src/pristine/sanakirja.rs at line 313
    [3.62979]
    [3.62979]
    let tags: UDb<L64, SerializedHash> = UDb::from_page(tup.0[4].into());
  • edit in libpijul/src/pristine/sanakirja.rs at line 322
    [3.63539]
    [3.539904]
    debug!("check: tags 0x{:x}", tags.db);
    ::sanakirja::debug::add_refs(&self.txn, &tags, &mut refs).unwrap();
  • edit in libpijul/src/pristine/sanakirja.rs at line 660
    [3.70500]
    [3.39127]
    pub tags: UDb<L64, SerializedHash>,
  • edit in libpijul/src/pristine/sanakirja.rs at line 848
    [3.8061]
    [3.74129]
    }
    }
    type Tags = UDb<L64, SerializedHash>;
    fn get_tags(
    &self,
    channel: &Self::Tags,
    c: &L64,
    ) -> Result<Option<&SerializedHash>, TxnErr<Self::GraphError>> {
    match btree::get(&self.txn, channel, c, None)? {
    Some((k, v)) if k == c => Ok(Some(v)),
    _ => Ok(None),
    }
    }
    type TagsCursor =
    ::sanakirja::btree::cursor::Cursor<L64, SerializedHash, UP<L64, SerializedHash>>;
    fn cursor_tags<'txn>(
    &'txn self,
    channel: &Self::Tags,
    k: Option<L64>,
    ) -> Result<
    crate::pristine::Cursor<Self, &'txn Self, Self::TagsCursor, L64, SerializedHash>,
    TxnErr<Self::GraphError>,
    > {
    let mut cursor = btree::cursor::Cursor::new(&self.txn, channel)?;
    if let Some(k) = k {
    cursor.set(&self.txn, &k, None)?;
    }
    Ok(Cursor {
    cursor,
    txn: self,
    k: std::marker::PhantomData,
    v: std::marker::PhantomData,
    t: std::marker::PhantomData,
    })
    }
    fn cursor_tags_next(
    &self,
    cursor: &mut Self::TagsCursor,
    ) -> Result<Option<(&L64, &SerializedHash)>, TxnErr<Self::GraphError>> {
    if let Ok(x) = cursor.next(&self.txn) {
    Ok(x)
    } else {
    Err(TxnErr(SanakirjaError::PristineCorrupt))
    }
    }
    fn cursor_tags_prev(
    &self,
    cursor: &mut Self::TagsCursor,
    ) -> Result<Option<(&L64, &SerializedHash)>, TxnErr<Self::GraphError>> {
    if let Ok(x) = cursor.prev(&self.txn) {
    Ok(x)
    } else {
    Err(TxnErr(SanakirjaError::PristineCorrupt))
  • replacement in libpijul/src/pristine/sanakirja.rs at line 1063
    [3.76627][3.76627:76731]()
    apply_counter: tup.0[4].into(),
    last_modified: tup.0[5].into(),
    [3.76627]
    [3.76731]
    tags: UDb::from_page(tup.0[4].into()),
    apply_counter: tup.0[5].into(),
    last_modified: tup.0[6].into(),
  • replacement in libpijul/src/pristine/sanakirja.rs at line 1218
    [3.551320][3.78958:79139]()
    type Channels = UDb<SmallStr, T6>;
    type ChannelsCursor = ::sanakirja::btree::cursor::Cursor<SmallStr, T6, UP<SmallStr, T6>>;
    sanakirja_cursor!(channels, SmallStr, T6,);
    [3.551320]
    [3.47694]
    type Channels = UDb<SmallStr, T8>;
    type ChannelsCursor = ::sanakirja::btree::cursor::Cursor<SmallStr, T8, UP<SmallStr, T8>>;
    sanakirja_cursor!(channels, SmallStr, T8,);
  • edit in libpijul/src/pristine/sanakirja.rs at line 1634
    [3.562092]
    [3.85274]
    btree::del(&mut self.txn, &mut channel.tags, &t.into(), None)?;
  • edit in libpijul/src/pristine/sanakirja.rs at line 1642
    [3.50936]
    [3.50936]
    fn put_tags(
    &mut self,
    channel: &mut Self::Channel,
    t: ApplyTimestamp,
    h: &Hash,
    ) -> Result<(), TxnErr<Self::GraphError>> {
    btree::put(&mut self.txn, &mut channel.tags, &t.into(), &h.into())?;
    Ok(())
    }
    fn del_tags(
    &mut self,
    channel: &mut Self::Channel,
    t: ApplyTimestamp,
    ) -> Result<(), TxnErr<Self::GraphError>> {
    btree::del(&mut self.txn, &mut channel.tags, &t.into(), None)?;
    Ok(())
    }
  • replacement in libpijul/src/pristine/sanakirja.rs at line 1760
    [3.407][3.407:465]()
    apply_counter: b.0[4].into(),
    [3.407]
    [3.465]
    tags: UDb::from_page(b.0[4].into()),
    apply_counter: b.0[5].into(),
    last_modified: b.0[6].into(),
  • edit in libpijul/src/pristine/sanakirja.rs at line 1764
    [3.513][3.513:571]()
    last_modified: b.0[5].into(),
  • edit in libpijul/src/pristine/sanakirja.rs at line 1773
    [3.11541]
    [3.11541]
    tags: btree::create_db_(&mut self.txn)?,
  • edit in libpijul/src/pristine/sanakirja.rs at line 1775
    [3.11591]
    [3.11591]
    last_modified: 0,
  • edit in libpijul/src/pristine/sanakirja.rs at line 1777
    [3.11643][3.11643:11693]()
    last_modified: 0,
  • edit in libpijul/src/pristine/sanakirja.rs at line 1817
    [3.89448]
    [3.89448]
    tags: btree::fork_db(&mut self.txn, &channel.tags)
    .map_err(|e| ForkError::Txn(e.into()))?,
  • replacement in libpijul/src/pristine/sanakirja.rs at line 2020
    [3.93724][3.93724:93824]()
    apply_counter: c.0[4].into(),
    last_modified: c.0[5].into(),
    [3.93724]
    [3.93824]
    tags: UDb::from_page(c.0[4].into()),
    apply_counter: c.0[5].into(),
    last_modified: c.0[6].into(),
  • replacement in libpijul/src/pristine/sanakirja.rs at line 2046
    [3.13754][3.94088:94110](),[3.573861][3.94088:94110]()
    let t6 = T6([
    [3.13754]
    [3.94110]
    let t8 = T8([
  • edit in libpijul/src/pristine/sanakirja.rs at line 2051
    [3.94266]
    [3.94266]
    channel.tags.db.into(),
  • edit in libpijul/src/pristine/sanakirja.rs at line 2054
    [3.94350]
    [3.94350]
    0u64.into(),
  • replacement in libpijul/src/pristine/sanakirja.rs at line 2056
    [3.94362][3.94362:94395](),[3.94395][3.13755:13831]()
    debug!("t6 = {:?}", t6);
    btree::put(&mut self.txn, &mut self.channels, &channel.name, &t6)?;
    [3.94362]
    [3.94488]
    btree::put(&mut self.txn, &mut self.channels, &channel.name, &t8)?;
  • replacement in libpijul/src/pristine/sanakirja.rs at line 2192
    [3.97832][3.97832:97850]()
    direct_repr!(T6);
    [3.97832]
    direct_repr!(T8);
  • replacement in libpijul/src/pristine/mod.rs at line 119
    [3.100861][3.100861:100886]()
    pub struct T6([L64; 6]);
    [3.100861]
    [3.100886]
    pub struct T8([L64; 8]);
  • edit in libpijul/src/pristine/mod.rs at line 342
    [3.102574]
    [3.51831]
    type Tags;
    fn get_tags(
    &self,
    channel: &Self::Tags,
    c: &L64,
    ) -> Result<Option<&SerializedHash>, TxnErr<Self::GraphError>>;
    type TagsCursor;
    fn cursor_tags<'txn>(
    &'txn self,
    channel: &Self::Tags,
    pos: Option<L64>,
    ) -> Result<
    crate::pristine::Cursor<Self, &'txn Self, Self::TagsCursor, L64, SerializedHash>,
    TxnErr<Self::GraphError>,
    >;
    fn cursor_tags_next(
    &self,
    cursor: &mut Self::TagsCursor,
    ) -> Result<Option<(&L64, &SerializedHash)>, TxnErr<Self::GraphError>>;
    fn cursor_tags_prev(
    &self,
    cursor: &mut Self::TagsCursor,
    ) -> Result<Option<(&L64, &SerializedHash)>, TxnErr<Self::GraphError>>;
  • replacement in libpijul/src/pristine/mod.rs at line 490
    [3.58221][3.103089:103126]()
    cursor!(channels, SmallStr, T6);
    [3.58221]
    [3.591136]
    cursor!(channels, SmallStr, T8);
  • edit in libpijul/src/pristine/mod.rs at line 1520
    [3.73114]
    [3.73114]
    fn put_tags(
    &mut self,
    channel: &mut Self::Channel,
    t: ApplyTimestamp,
    h: &Hash,
    ) -> Result<(), TxnErr<Self::GraphError>>;
    fn del_tags(
    &mut self,
    channel: &mut Self::Channel,
    t: ApplyTimestamp,
    ) -> Result<(), TxnErr<Self::GraphError>>;
  • edit in libpijul/src/lib.rs at line 31
    [3.717901]
    [3.717901]
    pub mod tag;
  • replacement in libpijul/src/lib.rs at line 100
    [3.60252][3.59713:59874]()
    Base32, ChangeId, ChannelRef, ChannelTxnT, DepsTxnT, EdgeFlags, GraphTxnT, Hash, Inode, Merkle,
    MutTxnT, OwnedPathId, RemoteRef, TreeTxnT, TxnT, Vertex,
    [3.60252]
    [3.60358]
    Base32, ChangeId, ChannelMutTxnT, ChannelRef, ChannelTxnT, DepsTxnT, EdgeFlags, GraphTxnT,
    Hash, Inode, Merkle, MutTxnT, OwnedPathId, RemoteRef, TreeTxnT, TxnT, Vertex,