##// END OF EJS Templates
wireprotoserver: ensure that output stream gets flushed on exception...
wireprotoserver: ensure that output stream gets flushed on exception Previously flush was happening due to Python finalizer being run on `BufferedWriter`. With upgrade to Python 3.11 this started randomly failing. My guess is that the finalizer on the raw `FileIO` object may be running before the finalizer of `BufferedWriter` has a chance to run. At any rate, since we're not relying on finalizers in the happy case we should also not rely on them in case of exception.

File last commit:

r51872:4c5f6e95 default
r52465:13c004b5 stable
Show More
main.rs
148 lines | 4.3 KiB | application/rls-services+xml | RustLexer
Georges Racinet
rust-nodemap: pure Rust example...
r44870 // 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::*;
Simon Sapin
rust: Switch to the memmap2-rs crate...
r48767 use memmap2::MmapOptions;
Georges Racinet
rust-nodemap: pure Rust example...
r44870 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() {
Raphaël Gomès
rust: make `Revision` a newtype...
r51872 let rev = Revision(rev as BaseRevision);
Georges Racinet
rust-nodemap: pure Rust example...
r44870 nm.insert(index, index.node(rev).unwrap(), rev).unwrap();
}
eprintln!("Nodemap constructed in RAM in {:?}", start.elapsed());
Raphaël Gomès
rust-clippy: use `write_all` since we're not expecting a partial write...
r50815 file.write_all(&nm.into_readonly_and_added_bytes().1)?;
Georges Racinet
rust-nodemap: pure Rust example...
r44870 eprintln!("Nodemap written to disk");
Ok(())
}
fn query(index: &Index, nm: &NodeTree, prefix: &str) {
let start = Instant::now();
Simon Sapin
rust: Remove hex parsing from the nodemap...
r47161 let res = NodePrefix::from_hex(prefix).map(|p| nm.find_bin(index, p));
Georges Racinet
rust-nodemap: pure Rust example...
r44870 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)
Raphaël Gomès
rust: make `Revision` a newtype...
r51872 .map(|_| {
*index
.node(Revision((rng.gen::<u32>() % len) as BaseRevision))
.unwrap()
})
Georges Racinet
rust-nodemap: pure Rust example...
r44870 .collect();
if queries < 10 {
let nodes_hex: Vec<String> =
Simon Sapin
rust: replace Node::encode_hex with std::fmt::LowerHex...
r47155 nodes.iter().map(|n| format!("{:x}", n)).collect();
Georges Racinet
rust-nodemap: pure Rust example...
r44870 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!(
Simon Sapin
rust: replace Node::encode_hex with std::fmt::LowerHex...
r47155 "Did {} queries in {:?} (mean {:?}), last was {:x} with result {:?}",
Georges Racinet
rust-nodemap: pure Rust example...
r44870 queries,
elapsed,
elapsed / (queries as u32),
Simon Sapin
rust: replace Node::encode_hex with std::fmt::LowerHex...
r47155 nodes.last().unwrap(),
Georges Racinet
rust-nodemap: pure Rust example...
r44870 last
);
}
fn main() {
Raphaël Gomès
hg-core: upgrade `clap` dependency...
r50531 use clap::{Parser, Subcommand};
Georges Racinet
rust-nodemap: pure Rust example...
r44870
Raphaël Gomès
hg-core: upgrade `clap` dependency...
r50531 #[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,
}
Georges Racinet
rust-nodemap: pure Rust example...
r44870
Raphaël Gomès
hg-core: upgrade `clap` dependency...
r50531 #[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 },
Georges Racinet
rust-nodemap: pure Rust example...
r44870 }
Raphaël Gomès
hg-core: upgrade `clap` dependency...
r50531 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()
);
Raphaël Gomès
rust: run `cargo clippy`...
r50809 create(&index, Path::new(nm_path)).unwrap();
Raphaël Gomès
hg-core: upgrade `clap` dependency...
r50531 }
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);
}
Georges Racinet
rust-nodemap: pure Rust example...
r44870 }
}