##// END OF EJS Templates
Added signature for changeset 6454c117c6a4
Added signature for changeset 6454c117c6a4

File last commit:

r51872:4c5f6e95 default
r52543:5b803e5c stable
Show More
main.rs
148 lines | 4.3 KiB | application/rls-services+xml | RustLexer
// Copyright 2019-2020 Georges Racinet <georges.racinet@octobus.net>
//
// This software may be used and distributed according to the terms of the
// GNU General Public License version 2 or any later version.
use hg::revlog::node::*;
use hg::revlog::nodemap::*;
use hg::revlog::*;
use memmap2::MmapOptions;
use rand::Rng;
use std::fs::File;
use std::io;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::time::Instant;
mod index;
use index::Index;
fn mmap_index(repo_path: &Path) -> Index {
let mut path = PathBuf::from(repo_path);
path.extend([".hg", "store", "00changelog.i"].iter());
Index::load_mmap(path)
}
fn mmap_nodemap(path: &Path) -> NodeTree {
let file = File::open(path).unwrap();
let mmap = unsafe { MmapOptions::new().map(&file).unwrap() };
let len = mmap.len();
NodeTree::load_bytes(Box::new(mmap), len)
}
/// Scan the whole index and create the corresponding nodemap file at `path`
fn create(index: &Index, path: &Path) -> io::Result<()> {
let mut file = File::create(path)?;
let start = Instant::now();
let mut nm = NodeTree::default();
for rev in 0..index.len() {
let rev = Revision(rev as BaseRevision);
nm.insert(index, index.node(rev).unwrap(), rev).unwrap();
}
eprintln!("Nodemap constructed in RAM in {:?}", start.elapsed());
file.write_all(&nm.into_readonly_and_added_bytes().1)?;
eprintln!("Nodemap written to disk");
Ok(())
}
fn query(index: &Index, nm: &NodeTree, prefix: &str) {
let start = Instant::now();
let res = NodePrefix::from_hex(prefix).map(|p| nm.find_bin(index, p));
println!("Result found in {:?}: {:?}", start.elapsed(), res);
}
fn bench(index: &Index, nm: &NodeTree, queries: usize) {
let len = index.len() as u32;
let mut rng = rand::thread_rng();
let nodes: Vec<Node> = (0..queries)
.map(|_| {
*index
.node(Revision((rng.gen::<u32>() % len) as BaseRevision))
.unwrap()
})
.collect();
if queries < 10 {
let nodes_hex: Vec<String> =
nodes.iter().map(|n| format!("{:x}", n)).collect();
println!("Nodes: {:?}", nodes_hex);
}
let mut last: Option<Revision> = None;
let start = Instant::now();
for node in nodes.iter() {
last = nm.find_bin(index, node.into()).unwrap();
}
let elapsed = start.elapsed();
println!(
"Did {} queries in {:?} (mean {:?}), last was {:x} with result {:?}",
queries,
elapsed,
elapsed / (queries as u32),
nodes.last().unwrap(),
last
);
}
fn main() {
use clap::{Parser, Subcommand};
#[derive(Parser)]
#[command()]
/// Nodemap pure Rust example
struct App {
// Path to the repository, always necessary for its index
#[arg(short, long)]
repository: PathBuf,
// Path to the nodemap file, independent of REPOSITORY
#[arg(short, long)]
nodemap_file: PathBuf,
#[command(subcommand)]
command: Command,
}
#[derive(Subcommand)]
enum Command {
/// Create `NODEMAP_FILE` by scanning repository index
Create,
/// Query `NODEMAP_FILE` for `prefix`
Query { prefix: String },
/// Perform #`QUERIES` random successful queries on `NODEMAP_FILE`
Bench { queries: usize },
}
let app = App::parse();
let repo = &app.repository;
let nm_path = &app.nodemap_file;
let index = mmap_index(repo);
let nm = mmap_nodemap(nm_path);
match &app.command {
Command::Create => {
println!(
"Creating nodemap file {} for repository {}",
nm_path.display(),
repo.display()
);
create(&index, Path::new(nm_path)).unwrap();
}
Command::Bench { queries } => {
println!(
"Doing {} random queries in nodemap file {} of repository {}",
queries,
nm_path.display(),
repo.display()
);
bench(&index, &nm, *queries);
}
Command::Query { prefix } => {
println!(
"Querying {} in nodemap file {} of repository {}",
prefix,
nm_path.display(),
repo.display()
);
query(&index, &nm, prefix);
}
}
}