File: main.rs

package info (click to toggle)
mercurial 7.1.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 45,080 kB
  • sloc: python: 208,589; ansic: 56,460; tcl: 3,715; sh: 1,839; lisp: 1,483; cpp: 864; makefile: 769; javascript: 649; xml: 36
file content (151 lines) | stat: -rw-r--r-- 4,447 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// 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 std::fs::File;
use std::io;
use std::io::Write;
use std::path::Path;
use std::path::PathBuf;
use std::time::Instant;

use hg::revlog::node::*;
use hg::revlog::nodemap::*;
use hg::revlog::*;
use memmap2::MmapOptions;
use rand::Rng;

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::rng();
    let nodes: Vec<Node> = (0..queries)
        .map(|_| {
            *index
                .node(Revision((rng.random::<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;
    use clap::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);
        }
    }
}