##// END OF EJS Templates
rust: Remove hex parsing from the nodemap...
Simon Sapin -
r47161:18a261b1 default
parent child Browse files
Show More
@@ -1,146 +1,146
1 1 // Copyright 2019-2020 Georges Racinet <georges.racinet@octobus.net>
2 2 //
3 3 // This software may be used and distributed according to the terms of the
4 4 // GNU General Public License version 2 or any later version.
5 5
6 6 use clap::*;
7 7 use hg::revlog::node::*;
8 8 use hg::revlog::nodemap::*;
9 9 use hg::revlog::*;
10 10 use memmap::MmapOptions;
11 11 use rand::Rng;
12 12 use std::fs::File;
13 13 use std::io;
14 14 use std::io::Write;
15 15 use std::path::{Path, PathBuf};
16 16 use std::str::FromStr;
17 17 use std::time::Instant;
18 18
19 19 mod index;
20 20 use index::Index;
21 21
22 22 fn mmap_index(repo_path: &Path) -> Index {
23 23 let mut path = PathBuf::from(repo_path);
24 24 path.extend([".hg", "store", "00changelog.i"].iter());
25 25 Index::load_mmap(path)
26 26 }
27 27
28 28 fn mmap_nodemap(path: &Path) -> NodeTree {
29 29 let file = File::open(path).unwrap();
30 30 let mmap = unsafe { MmapOptions::new().map(&file).unwrap() };
31 31 let len = mmap.len();
32 32 NodeTree::load_bytes(Box::new(mmap), len)
33 33 }
34 34
35 35 /// Scan the whole index and create the corresponding nodemap file at `path`
36 36 fn create(index: &Index, path: &Path) -> io::Result<()> {
37 37 let mut file = File::create(path)?;
38 38 let start = Instant::now();
39 39 let mut nm = NodeTree::default();
40 40 for rev in 0..index.len() {
41 41 let rev = rev as Revision;
42 42 nm.insert(index, index.node(rev).unwrap(), rev).unwrap();
43 43 }
44 44 eprintln!("Nodemap constructed in RAM in {:?}", start.elapsed());
45 45 file.write(&nm.into_readonly_and_added_bytes().1)?;
46 46 eprintln!("Nodemap written to disk");
47 47 Ok(())
48 48 }
49 49
50 50 fn query(index: &Index, nm: &NodeTree, prefix: &str) {
51 51 let start = Instant::now();
52 let res = nm.find_hex(index, prefix);
52 let res = NodePrefix::from_hex(prefix).map(|p| nm.find_bin(index, p));
53 53 println!("Result found in {:?}: {:?}", start.elapsed(), res);
54 54 }
55 55
56 56 fn bench(index: &Index, nm: &NodeTree, queries: usize) {
57 57 let len = index.len() as u32;
58 58 let mut rng = rand::thread_rng();
59 59 let nodes: Vec<Node> = (0..queries)
60 60 .map(|_| {
61 61 index
62 62 .node((rng.gen::<u32>() % len) as Revision)
63 63 .unwrap()
64 64 .clone()
65 65 })
66 66 .collect();
67 67 if queries < 10 {
68 68 let nodes_hex: Vec<String> =
69 69 nodes.iter().map(|n| format!("{:x}", n)).collect();
70 70 println!("Nodes: {:?}", nodes_hex);
71 71 }
72 72 let mut last: Option<Revision> = None;
73 73 let start = Instant::now();
74 74 for node in nodes.iter() {
75 75 last = nm.find_bin(index, node.into()).unwrap();
76 76 }
77 77 let elapsed = start.elapsed();
78 78 println!(
79 79 "Did {} queries in {:?} (mean {:?}), last was {:x} with result {:?}",
80 80 queries,
81 81 elapsed,
82 82 elapsed / (queries as u32),
83 83 nodes.last().unwrap(),
84 84 last
85 85 );
86 86 }
87 87
88 88 fn main() {
89 89 let matches = App::new("Nodemap pure Rust example")
90 90 .arg(
91 91 Arg::with_name("REPOSITORY")
92 92 .help("Path to the repository, always necessary for its index")
93 93 .required(true),
94 94 )
95 95 .arg(
96 96 Arg::with_name("NODEMAP_FILE")
97 97 .help("Path to the nodemap file, independent of REPOSITORY")
98 98 .required(true),
99 99 )
100 100 .subcommand(
101 101 SubCommand::with_name("create")
102 102 .about("Create NODEMAP_FILE by scanning repository index"),
103 103 )
104 104 .subcommand(
105 105 SubCommand::with_name("query")
106 106 .about("Query NODEMAP_FILE for PREFIX")
107 107 .arg(Arg::with_name("PREFIX").required(true)),
108 108 )
109 109 .subcommand(
110 110 SubCommand::with_name("bench")
111 111 .about(
112 112 "Perform #QUERIES random successful queries on NODEMAP_FILE")
113 113 .arg(Arg::with_name("QUERIES").required(true)),
114 114 )
115 115 .get_matches();
116 116
117 117 let repo = matches.value_of("REPOSITORY").unwrap();
118 118 let nm_path = matches.value_of("NODEMAP_FILE").unwrap();
119 119
120 120 let index = mmap_index(&Path::new(repo));
121 121
122 122 if let Some(_) = matches.subcommand_matches("create") {
123 123 println!("Creating nodemap file {} for repository {}", nm_path, repo);
124 124 create(&index, &Path::new(nm_path)).unwrap();
125 125 return;
126 126 }
127 127
128 128 let nm = mmap_nodemap(&Path::new(nm_path));
129 129 if let Some(matches) = matches.subcommand_matches("query") {
130 130 let prefix = matches.value_of("PREFIX").unwrap();
131 131 println!(
132 132 "Querying {} in nodemap file {} of repository {}",
133 133 prefix, nm_path, repo
134 134 );
135 135 query(&index, &nm, prefix);
136 136 }
137 137 if let Some(matches) = matches.subcommand_matches("bench") {
138 138 let queries =
139 139 usize::from_str(matches.value_of("QUERIES").unwrap()).unwrap();
140 140 println!(
141 141 "Doing {} random queries in nodemap file {} of repository {}",
142 142 queries, nm_path, repo
143 143 );
144 144 bench(&index, &nm, queries);
145 145 }
146 146 }
@@ -1,1098 +1,1069
1 1 // Copyright 2018-2020 Georges Racinet <georges.racinet@octobus.net>
2 2 // and Mercurial contributors
3 3 //
4 4 // This software may be used and distributed according to the terms of the
5 5 // GNU General Public License version 2 or any later version.
6 6 //! Indexing facilities for fast retrieval of `Revision` from `Node`
7 7 //!
8 8 //! This provides a variation on the 16-ary radix tree that is
9 9 //! provided as "nodetree" in revlog.c, ready for append-only persistence
10 10 //! on disk.
11 11 //!
12 12 //! Following existing implicit conventions, the "nodemap" terminology
13 13 //! is used in a more abstract context.
14 14
15 15 use super::{
16 node::NULL_NODE, FromHexError, Node, NodePrefix, Revision, RevlogIndex,
17 NULL_REVISION,
16 node::NULL_NODE, Node, NodePrefix, Revision, RevlogIndex, NULL_REVISION,
18 17 };
19 18
20 19 use bytes_cast::{unaligned, BytesCast};
21 20 use std::cmp::max;
22 21 use std::fmt;
23 22 use std::mem::{self, align_of, size_of};
24 23 use std::ops::Deref;
25 24 use std::ops::Index;
26 25
27 26 #[derive(Debug, PartialEq)]
28 27 pub enum NodeMapError {
29 28 MultipleResults,
30 InvalidNodePrefix,
31 29 /// A `Revision` stored in the nodemap could not be found in the index
32 30 RevisionNotInIndex(Revision),
33 31 }
34 32
35 impl From<FromHexError> for NodeMapError {
36 fn from(_: FromHexError) -> Self {
37 NodeMapError::InvalidNodePrefix
38 }
39 }
40
41 33 /// Mapping system from Mercurial nodes to revision numbers.
42 34 ///
43 35 /// ## `RevlogIndex` and `NodeMap`
44 36 ///
45 37 /// One way to think about their relationship is that
46 38 /// the `NodeMap` is a prefix-oriented reverse index of the `Node` information
47 39 /// carried by a [`RevlogIndex`].
48 40 ///
49 41 /// Many of the methods in this trait take a `RevlogIndex` argument
50 42 /// which is used for validation of their results. This index must naturally
51 43 /// be the one the `NodeMap` is about, and it must be consistent.
52 44 ///
53 45 /// Notably, the `NodeMap` must not store
54 46 /// information about more `Revision` values than there are in the index.
55 47 /// In these methods, an encountered `Revision` is not in the index, a
56 48 /// [`RevisionNotInIndex`] error is returned.
57 49 ///
58 50 /// In insert operations, the rule is thus that the `NodeMap` must always
59 51 /// be updated after the `RevlogIndex`
60 52 /// be updated first, and the `NodeMap` second.
61 53 ///
62 54 /// [`RevisionNotInIndex`]: enum.NodeMapError.html#variant.RevisionNotInIndex
63 55 /// [`RevlogIndex`]: ../trait.RevlogIndex.html
64 56 pub trait NodeMap {
65 57 /// Find the unique `Revision` having the given `Node`
66 58 ///
67 59 /// If no Revision matches the given `Node`, `Ok(None)` is returned.
68 60 fn find_node(
69 61 &self,
70 62 index: &impl RevlogIndex,
71 63 node: &Node,
72 64 ) -> Result<Option<Revision>, NodeMapError> {
73 65 self.find_bin(index, node.into())
74 66 }
75 67
76 68 /// Find the unique Revision whose `Node` starts with a given binary prefix
77 69 ///
78 70 /// If no Revision matches the given prefix, `Ok(None)` is returned.
79 71 ///
80 72 /// If several Revisions match the given prefix, a [`MultipleResults`]
81 73 /// error is returned.
82 74 fn find_bin<'a>(
83 75 &self,
84 76 idx: &impl RevlogIndex,
85 77 prefix: NodePrefix,
86 78 ) -> Result<Option<Revision>, NodeMapError>;
87 79
88 /// Find the unique Revision whose `Node` hexadecimal string representation
89 /// starts with a given prefix
90 ///
91 /// If no Revision matches the given prefix, `Ok(None)` is returned.
92 ///
93 /// If several Revisions match the given prefix, a [`MultipleResults`]
94 /// error is returned.
95 fn find_hex(
96 &self,
97 idx: &impl RevlogIndex,
98 prefix: &str,
99 ) -> Result<Option<Revision>, NodeMapError> {
100 self.find_bin(idx, NodePrefix::from_hex(prefix)?)
101 }
102
103 80 /// Give the size of the shortest node prefix that determines
104 81 /// the revision uniquely.
105 82 ///
106 83 /// From a binary node prefix, if it is matched in the node map, this
107 84 /// returns the number of hexadecimal digits that would had sufficed
108 85 /// to find the revision uniquely.
109 86 ///
110 87 /// Returns `None` if no `Revision` could be found for the prefix.
111 88 ///
112 89 /// If several Revisions match the given prefix, a [`MultipleResults`]
113 90 /// error is returned.
114 91 fn unique_prefix_len_bin<'a>(
115 92 &self,
116 93 idx: &impl RevlogIndex,
117 94 node_prefix: NodePrefix,
118 95 ) -> Result<Option<usize>, NodeMapError>;
119 96
120 /// Same as `unique_prefix_len_bin`, with the hexadecimal representation
121 /// of the prefix as input.
122 fn unique_prefix_len_hex(
123 &self,
124 idx: &impl RevlogIndex,
125 prefix: &str,
126 ) -> Result<Option<usize>, NodeMapError> {
127 self.unique_prefix_len_bin(idx, NodePrefix::from_hex(prefix)?)
128 }
129
130 97 /// Same as `unique_prefix_len_bin`, with a full `Node` as input
131 98 fn unique_prefix_len_node(
132 99 &self,
133 100 idx: &impl RevlogIndex,
134 101 node: &Node,
135 102 ) -> Result<Option<usize>, NodeMapError> {
136 103 self.unique_prefix_len_bin(idx, node.into())
137 104 }
138 105 }
139 106
140 107 pub trait MutableNodeMap: NodeMap {
141 108 fn insert<I: RevlogIndex>(
142 109 &mut self,
143 110 index: &I,
144 111 node: &Node,
145 112 rev: Revision,
146 113 ) -> Result<(), NodeMapError>;
147 114 }
148 115
149 116 /// Low level NodeTree [`Blocks`] elements
150 117 ///
151 118 /// These are exactly as for instance on persistent storage.
152 119 type RawElement = unaligned::I32Be;
153 120
154 121 /// High level representation of values in NodeTree
155 122 /// [`Blocks`](struct.Block.html)
156 123 ///
157 124 /// This is the high level representation that most algorithms should
158 125 /// use.
159 126 #[derive(Clone, Debug, Eq, PartialEq)]
160 127 enum Element {
161 128 Rev(Revision),
162 129 Block(usize),
163 130 None,
164 131 }
165 132
166 133 impl From<RawElement> for Element {
167 134 /// Conversion from low level representation, after endianness conversion.
168 135 ///
169 136 /// See [`Block`](struct.Block.html) for explanation about the encoding.
170 137 fn from(raw: RawElement) -> Element {
171 138 let int = raw.get();
172 139 if int >= 0 {
173 140 Element::Block(int as usize)
174 141 } else if int == -1 {
175 142 Element::None
176 143 } else {
177 144 Element::Rev(-int - 2)
178 145 }
179 146 }
180 147 }
181 148
182 149 impl From<Element> for RawElement {
183 150 fn from(element: Element) -> RawElement {
184 151 RawElement::from(match element {
185 152 Element::None => 0,
186 153 Element::Block(i) => i as i32,
187 154 Element::Rev(rev) => -rev - 2,
188 155 })
189 156 }
190 157 }
191 158
192 159 /// A logical block of the `NodeTree`, packed with a fixed size.
193 160 ///
194 161 /// These are always used in container types implementing `Index<Block>`,
195 162 /// such as `&Block`
196 163 ///
197 164 /// As an array of integers, its ith element encodes that the
198 165 /// ith potential edge from the block, representing the ith hexadecimal digit
199 166 /// (nybble) `i` is either:
200 167 ///
201 168 /// - absent (value -1)
202 169 /// - another `Block` in the same indexable container (value β‰₯ 0)
203 170 /// - a `Revision` leaf (value ≀ -2)
204 171 ///
205 172 /// Endianness has to be fixed for consistency on shared storage across
206 173 /// different architectures.
207 174 ///
208 175 /// A key difference with the C `nodetree` is that we need to be
209 176 /// able to represent the [`Block`] at index 0, hence -1 is the empty marker
210 177 /// rather than 0 and the `Revision` range upper limit of -2 instead of -1.
211 178 ///
212 179 /// Another related difference is that `NULL_REVISION` (-1) is not
213 180 /// represented at all, because we want an immutable empty nodetree
214 181 /// to be valid.
215 182
216 183 const ELEMENTS_PER_BLOCK: usize = 16; // number of different values in a nybble
217 184
218 185 #[derive(Copy, Clone, BytesCast, PartialEq)]
219 186 #[repr(transparent)]
220 187 pub struct Block([RawElement; ELEMENTS_PER_BLOCK]);
221 188
222 189 impl Block {
223 190 fn new() -> Self {
224 191 let absent_node = RawElement::from(-1);
225 192 Block([absent_node; ELEMENTS_PER_BLOCK])
226 193 }
227 194
228 195 fn get(&self, nybble: u8) -> Element {
229 196 self.0[nybble as usize].into()
230 197 }
231 198
232 199 fn set(&mut self, nybble: u8, element: Element) {
233 200 self.0[nybble as usize] = element.into()
234 201 }
235 202 }
236 203
237 204 impl fmt::Debug for Block {
238 205 /// sparse representation for testing and debugging purposes
239 206 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
240 207 f.debug_map()
241 208 .entries((0..16).filter_map(|i| match self.get(i) {
242 209 Element::None => None,
243 210 element => Some((i, element)),
244 211 }))
245 212 .finish()
246 213 }
247 214 }
248 215
249 216 /// A mutable 16-radix tree with the root block logically at the end
250 217 ///
251 218 /// Because of the append only nature of our node trees, we need to
252 219 /// keep the original untouched and store new blocks separately.
253 220 ///
254 221 /// The mutable root `Block` is kept apart so that we don't have to rebump
255 222 /// it on each insertion.
256 223 pub struct NodeTree {
257 224 readonly: Box<dyn Deref<Target = [Block]> + Send>,
258 225 growable: Vec<Block>,
259 226 root: Block,
260 227 masked_inner_blocks: usize,
261 228 }
262 229
263 230 impl Index<usize> for NodeTree {
264 231 type Output = Block;
265 232
266 233 fn index(&self, i: usize) -> &Block {
267 234 let ro_len = self.readonly.len();
268 235 if i < ro_len {
269 236 &self.readonly[i]
270 237 } else if i == ro_len + self.growable.len() {
271 238 &self.root
272 239 } else {
273 240 &self.growable[i - ro_len]
274 241 }
275 242 }
276 243 }
277 244
278 245 /// Return `None` unless the `Node` for `rev` has given prefix in `index`.
279 246 fn has_prefix_or_none(
280 247 idx: &impl RevlogIndex,
281 248 prefix: NodePrefix,
282 249 rev: Revision,
283 250 ) -> Result<Option<Revision>, NodeMapError> {
284 251 idx.node(rev)
285 252 .ok_or_else(|| NodeMapError::RevisionNotInIndex(rev))
286 253 .map(|node| {
287 254 if prefix.is_prefix_of(node) {
288 255 Some(rev)
289 256 } else {
290 257 None
291 258 }
292 259 })
293 260 }
294 261
295 262 /// validate that the candidate's node starts indeed with given prefix,
296 263 /// and treat ambiguities related to `NULL_REVISION`.
297 264 ///
298 265 /// From the data in the NodeTree, one can only conclude that some
299 266 /// revision is the only one for a *subprefix* of the one being looked up.
300 267 fn validate_candidate(
301 268 idx: &impl RevlogIndex,
302 269 prefix: NodePrefix,
303 270 candidate: (Option<Revision>, usize),
304 271 ) -> Result<(Option<Revision>, usize), NodeMapError> {
305 272 let (rev, steps) = candidate;
306 273 if let Some(nz_nybble) = prefix.first_different_nybble(&NULL_NODE) {
307 274 rev.map_or(Ok((None, steps)), |r| {
308 275 has_prefix_or_none(idx, prefix, r)
309 276 .map(|opt| (opt, max(steps, nz_nybble + 1)))
310 277 })
311 278 } else {
312 279 // the prefix is only made of zeros; NULL_REVISION always matches it
313 280 // and any other *valid* result is an ambiguity
314 281 match rev {
315 282 None => Ok((Some(NULL_REVISION), steps + 1)),
316 283 Some(r) => match has_prefix_or_none(idx, prefix, r)? {
317 284 None => Ok((Some(NULL_REVISION), steps + 1)),
318 285 _ => Err(NodeMapError::MultipleResults),
319 286 },
320 287 }
321 288 }
322 289 }
323 290
324 291 impl NodeTree {
325 292 /// Initiate a NodeTree from an immutable slice-like of `Block`
326 293 ///
327 294 /// We keep `readonly` and clone its root block if it isn't empty.
328 295 fn new(readonly: Box<dyn Deref<Target = [Block]> + Send>) -> Self {
329 296 let root = readonly.last().cloned().unwrap_or_else(Block::new);
330 297 NodeTree {
331 298 readonly,
332 299 growable: Vec::new(),
333 300 root,
334 301 masked_inner_blocks: 0,
335 302 }
336 303 }
337 304
338 305 /// Create from an opaque bunch of bytes
339 306 ///
340 307 /// The created `NodeTreeBytes` from `buffer`,
341 308 /// of which exactly `amount` bytes are used.
342 309 ///
343 310 /// - `buffer` could be derived from `PyBuffer` and `Mmap` objects.
344 311 /// - `offset` allows for the final file format to include fixed data
345 312 /// (generation number, behavioural flags)
346 313 /// - `amount` is expressed in bytes, and is not automatically derived from
347 314 /// `bytes`, so that a caller that manages them atomically can perform
348 315 /// temporary disk serializations and still rollback easily if needed.
349 316 /// First use-case for this would be to support Mercurial shell hooks.
350 317 ///
351 318 /// panics if `buffer` is smaller than `amount`
352 319 pub fn load_bytes(
353 320 bytes: Box<dyn Deref<Target = [u8]> + Send>,
354 321 amount: usize,
355 322 ) -> Self {
356 323 NodeTree::new(Box::new(NodeTreeBytes::new(bytes, amount)))
357 324 }
358 325
359 326 /// Retrieve added `Block` and the original immutable data
360 327 pub fn into_readonly_and_added(
361 328 self,
362 329 ) -> (Box<dyn Deref<Target = [Block]> + Send>, Vec<Block>) {
363 330 let mut vec = self.growable;
364 331 let readonly = self.readonly;
365 332 if readonly.last() != Some(&self.root) {
366 333 vec.push(self.root);
367 334 }
368 335 (readonly, vec)
369 336 }
370 337
371 338 /// Retrieve added `Blocks` as bytes, ready to be written to persistent
372 339 /// storage
373 340 pub fn into_readonly_and_added_bytes(
374 341 self,
375 342 ) -> (Box<dyn Deref<Target = [Block]> + Send>, Vec<u8>) {
376 343 let (readonly, vec) = self.into_readonly_and_added();
377 344 // Prevent running `v`'s destructor so we are in complete control
378 345 // of the allocation.
379 346 let vec = mem::ManuallyDrop::new(vec);
380 347
381 348 // Transmute the `Vec<Block>` to a `Vec<u8>`. Blocks are contiguous
382 349 // bytes, so this is perfectly safe.
383 350 let bytes = unsafe {
384 351 // Check for compatible allocation layout.
385 352 // (Optimized away by constant-folding + dead code elimination.)
386 353 assert_eq!(size_of::<Block>(), 64);
387 354 assert_eq!(align_of::<Block>(), 1);
388 355
389 356 // /!\ Any use of `vec` after this is use-after-free.
390 357 // TODO: use `into_raw_parts` once stabilized
391 358 Vec::from_raw_parts(
392 359 vec.as_ptr() as *mut u8,
393 360 vec.len() * size_of::<Block>(),
394 361 vec.capacity() * size_of::<Block>(),
395 362 )
396 363 };
397 364 (readonly, bytes)
398 365 }
399 366
400 367 /// Total number of blocks
401 368 fn len(&self) -> usize {
402 369 self.readonly.len() + self.growable.len() + 1
403 370 }
404 371
405 372 /// Implemented for completeness
406 373 ///
407 374 /// A `NodeTree` always has at least the mutable root block.
408 375 #[allow(dead_code)]
409 376 fn is_empty(&self) -> bool {
410 377 false
411 378 }
412 379
413 380 /// Main working method for `NodeTree` searches
414 381 ///
415 382 /// The first returned value is the result of analysing `NodeTree` data
416 383 /// *alone*: whereas `None` guarantees that the given prefix is absent
417 384 /// from the `NodeTree` data (but still could match `NULL_NODE`), with
418 385 /// `Some(rev)`, it is to be understood that `rev` is the unique `Revision`
419 386 /// that could match the prefix. Actually, all that can be inferred from
420 387 /// the `NodeTree` data is that `rev` is the revision with the longest
421 388 /// common node prefix with the given prefix.
422 389 ///
423 390 /// The second returned value is the size of the smallest subprefix
424 391 /// of `prefix` that would give the same result, i.e. not the
425 392 /// `MultipleResults` error variant (again, using only the data of the
426 393 /// `NodeTree`).
427 394 fn lookup(
428 395 &self,
429 396 prefix: NodePrefix,
430 397 ) -> Result<(Option<Revision>, usize), NodeMapError> {
431 398 for (i, visit_item) in self.visit(prefix).enumerate() {
432 399 if let Some(opt) = visit_item.final_revision() {
433 400 return Ok((opt, i + 1));
434 401 }
435 402 }
436 403 Err(NodeMapError::MultipleResults)
437 404 }
438 405
439 406 fn visit<'n>(&'n self, prefix: NodePrefix) -> NodeTreeVisitor<'n> {
440 407 NodeTreeVisitor {
441 408 nt: self,
442 409 prefix,
443 410 visit: self.len() - 1,
444 411 nybble_idx: 0,
445 412 done: false,
446 413 }
447 414 }
448 415 /// Return a mutable reference for `Block` at index `idx`.
449 416 ///
450 417 /// If `idx` lies in the immutable area, then the reference is to
451 418 /// a newly appended copy.
452 419 ///
453 420 /// Returns (new_idx, glen, mut_ref) where
454 421 ///
455 422 /// - `new_idx` is the index of the mutable `Block`
456 423 /// - `mut_ref` is a mutable reference to the mutable Block.
457 424 /// - `glen` is the new length of `self.growable`
458 425 ///
459 426 /// Note: the caller wouldn't be allowed to query `self.growable.len()`
460 427 /// itself because of the mutable borrow taken with the returned `Block`
461 428 fn mutable_block(&mut self, idx: usize) -> (usize, &mut Block, usize) {
462 429 let ro_blocks = &self.readonly;
463 430 let ro_len = ro_blocks.len();
464 431 let glen = self.growable.len();
465 432 if idx < ro_len {
466 433 self.masked_inner_blocks += 1;
467 434 self.growable.push(ro_blocks[idx]);
468 435 (glen + ro_len, &mut self.growable[glen], glen + 1)
469 436 } else if glen + ro_len == idx {
470 437 (idx, &mut self.root, glen)
471 438 } else {
472 439 (idx, &mut self.growable[idx - ro_len], glen)
473 440 }
474 441 }
475 442
476 443 /// Main insertion method
477 444 ///
478 445 /// This will dive in the node tree to find the deepest `Block` for
479 446 /// `node`, split it as much as needed and record `node` in there.
480 447 /// The method then backtracks, updating references in all the visited
481 448 /// blocks from the root.
482 449 ///
483 450 /// All the mutated `Block` are copied first to the growable part if
484 451 /// needed. That happens for those in the immutable part except the root.
485 452 pub fn insert<I: RevlogIndex>(
486 453 &mut self,
487 454 index: &I,
488 455 node: &Node,
489 456 rev: Revision,
490 457 ) -> Result<(), NodeMapError> {
491 458 let ro_len = &self.readonly.len();
492 459
493 460 let mut visit_steps: Vec<_> = self.visit(node.into()).collect();
494 461 let read_nybbles = visit_steps.len();
495 462 // visit_steps cannot be empty, since we always visit the root block
496 463 let deepest = visit_steps.pop().unwrap();
497 464
498 465 let (mut block_idx, mut block, mut glen) =
499 466 self.mutable_block(deepest.block_idx);
500 467
501 468 if let Element::Rev(old_rev) = deepest.element {
502 469 let old_node = index
503 470 .node(old_rev)
504 471 .ok_or_else(|| NodeMapError::RevisionNotInIndex(old_rev))?;
505 472 if old_node == node {
506 473 return Ok(()); // avoid creating lots of useless blocks
507 474 }
508 475
509 476 // Looping over the tail of nybbles in both nodes, creating
510 477 // new blocks until we find the difference
511 478 let mut new_block_idx = ro_len + glen;
512 479 let mut nybble = deepest.nybble;
513 480 for nybble_pos in read_nybbles..node.nybbles_len() {
514 481 block.set(nybble, Element::Block(new_block_idx));
515 482
516 483 let new_nybble = node.get_nybble(nybble_pos);
517 484 let old_nybble = old_node.get_nybble(nybble_pos);
518 485
519 486 if old_nybble == new_nybble {
520 487 self.growable.push(Block::new());
521 488 block = &mut self.growable[glen];
522 489 glen += 1;
523 490 new_block_idx += 1;
524 491 nybble = new_nybble;
525 492 } else {
526 493 let mut new_block = Block::new();
527 494 new_block.set(old_nybble, Element::Rev(old_rev));
528 495 new_block.set(new_nybble, Element::Rev(rev));
529 496 self.growable.push(new_block);
530 497 break;
531 498 }
532 499 }
533 500 } else {
534 501 // Free slot in the deepest block: no splitting has to be done
535 502 block.set(deepest.nybble, Element::Rev(rev));
536 503 }
537 504
538 505 // Backtrack over visit steps to update references
539 506 while let Some(visited) = visit_steps.pop() {
540 507 let to_write = Element::Block(block_idx);
541 508 if visit_steps.is_empty() {
542 509 self.root.set(visited.nybble, to_write);
543 510 break;
544 511 }
545 512 let (new_idx, block, _) = self.mutable_block(visited.block_idx);
546 513 if block.get(visited.nybble) == to_write {
547 514 break;
548 515 }
549 516 block.set(visited.nybble, to_write);
550 517 block_idx = new_idx;
551 518 }
552 519 Ok(())
553 520 }
554 521
555 522 /// Make the whole `NodeTree` logically empty, without touching the
556 523 /// immutable part.
557 524 pub fn invalidate_all(&mut self) {
558 525 self.root = Block::new();
559 526 self.growable = Vec::new();
560 527 self.masked_inner_blocks = self.readonly.len();
561 528 }
562 529
563 530 /// Return the number of blocks in the readonly part that are currently
564 531 /// masked in the mutable part.
565 532 ///
566 533 /// The `NodeTree` structure has no efficient way to know how many blocks
567 534 /// are already unreachable in the readonly part.
568 535 ///
569 536 /// After a call to `invalidate_all()`, the returned number can be actually
570 537 /// bigger than the whole readonly part, a conventional way to mean that
571 538 /// all the readonly blocks have been masked. This is what is really
572 539 /// useful to the caller and does not require to know how many were
573 540 /// actually unreachable to begin with.
574 541 pub fn masked_readonly_blocks(&self) -> usize {
575 542 if let Some(readonly_root) = self.readonly.last() {
576 543 if readonly_root == &self.root {
577 544 return 0;
578 545 }
579 546 } else {
580 547 return 0;
581 548 }
582 549 self.masked_inner_blocks + 1
583 550 }
584 551 }
585 552
586 553 pub struct NodeTreeBytes {
587 554 buffer: Box<dyn Deref<Target = [u8]> + Send>,
588 555 len_in_blocks: usize,
589 556 }
590 557
591 558 impl NodeTreeBytes {
592 559 fn new(
593 560 buffer: Box<dyn Deref<Target = [u8]> + Send>,
594 561 amount: usize,
595 562 ) -> Self {
596 563 assert!(buffer.len() >= amount);
597 564 let len_in_blocks = amount / size_of::<Block>();
598 565 NodeTreeBytes {
599 566 buffer,
600 567 len_in_blocks,
601 568 }
602 569 }
603 570 }
604 571
605 572 impl Deref for NodeTreeBytes {
606 573 type Target = [Block];
607 574
608 575 fn deref(&self) -> &[Block] {
609 576 Block::slice_from_bytes(&self.buffer, self.len_in_blocks)
610 577 // `NodeTreeBytes::new` already asserted that `self.buffer` is
611 578 // large enough.
612 579 .unwrap()
613 580 .0
614 581 }
615 582 }
616 583
617 584 struct NodeTreeVisitor<'n> {
618 585 nt: &'n NodeTree,
619 586 prefix: NodePrefix,
620 587 visit: usize,
621 588 nybble_idx: usize,
622 589 done: bool,
623 590 }
624 591
625 592 #[derive(Debug, PartialEq, Clone)]
626 593 struct NodeTreeVisitItem {
627 594 block_idx: usize,
628 595 nybble: u8,
629 596 element: Element,
630 597 }
631 598
632 599 impl<'n> Iterator for NodeTreeVisitor<'n> {
633 600 type Item = NodeTreeVisitItem;
634 601
635 602 fn next(&mut self) -> Option<Self::Item> {
636 603 if self.done || self.nybble_idx >= self.prefix.nybbles_len() {
637 604 return None;
638 605 }
639 606
640 607 let nybble = self.prefix.get_nybble(self.nybble_idx);
641 608 self.nybble_idx += 1;
642 609
643 610 let visit = self.visit;
644 611 let element = self.nt[visit].get(nybble);
645 612 if let Element::Block(idx) = element {
646 613 self.visit = idx;
647 614 } else {
648 615 self.done = true;
649 616 }
650 617
651 618 Some(NodeTreeVisitItem {
652 619 block_idx: visit,
653 620 nybble,
654 621 element,
655 622 })
656 623 }
657 624 }
658 625
659 626 impl NodeTreeVisitItem {
660 627 // Return `Some(opt)` if this item is final, with `opt` being the
661 628 // `Revision` that it may represent.
662 629 //
663 630 // If the item is not terminal, return `None`
664 631 fn final_revision(&self) -> Option<Option<Revision>> {
665 632 match self.element {
666 633 Element::Block(_) => None,
667 634 Element::Rev(r) => Some(Some(r)),
668 635 Element::None => Some(None),
669 636 }
670 637 }
671 638 }
672 639
673 640 impl From<Vec<Block>> for NodeTree {
674 641 fn from(vec: Vec<Block>) -> Self {
675 642 Self::new(Box::new(vec))
676 643 }
677 644 }
678 645
679 646 impl fmt::Debug for NodeTree {
680 647 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
681 648 let readonly: &[Block] = &*self.readonly;
682 649 write!(
683 650 f,
684 651 "readonly: {:?}, growable: {:?}, root: {:?}",
685 652 readonly, self.growable, self.root
686 653 )
687 654 }
688 655 }
689 656
690 657 impl Default for NodeTree {
691 658 /// Create a fully mutable empty NodeTree
692 659 fn default() -> Self {
693 660 NodeTree::new(Box::new(Vec::new()))
694 661 }
695 662 }
696 663
697 664 impl NodeMap for NodeTree {
698 665 fn find_bin<'a>(
699 666 &self,
700 667 idx: &impl RevlogIndex,
701 668 prefix: NodePrefix,
702 669 ) -> Result<Option<Revision>, NodeMapError> {
703 670 validate_candidate(idx, prefix, self.lookup(prefix)?)
704 671 .map(|(opt, _shortest)| opt)
705 672 }
706 673
707 674 fn unique_prefix_len_bin<'a>(
708 675 &self,
709 676 idx: &impl RevlogIndex,
710 677 prefix: NodePrefix,
711 678 ) -> Result<Option<usize>, NodeMapError> {
712 679 validate_candidate(idx, prefix, self.lookup(prefix)?)
713 680 .map(|(opt, shortest)| opt.map(|_rev| shortest))
714 681 }
715 682 }
716 683
717 684 #[cfg(test)]
718 685 mod tests {
719 686 use super::NodeMapError::*;
720 687 use super::*;
721 688 use crate::revlog::node::{hex_pad_right, Node};
722 689 use std::collections::HashMap;
723 690
724 691 /// Creates a `Block` using a syntax close to the `Debug` output
725 692 macro_rules! block {
726 693 {$($nybble:tt : $variant:ident($val:tt)),*} => (
727 694 {
728 695 let mut block = Block::new();
729 696 $(block.set($nybble, Element::$variant($val)));*;
730 697 block
731 698 }
732 699 )
733 700 }
734 701
735 702 #[test]
736 703 fn test_block_debug() {
737 704 let mut block = Block::new();
738 705 block.set(1, Element::Rev(3));
739 706 block.set(10, Element::Block(0));
740 707 assert_eq!(format!("{:?}", block), "{1: Rev(3), 10: Block(0)}");
741 708 }
742 709
743 710 #[test]
744 711 fn test_block_macro() {
745 712 let block = block! {5: Block(2)};
746 713 assert_eq!(format!("{:?}", block), "{5: Block(2)}");
747 714
748 715 let block = block! {13: Rev(15), 5: Block(2)};
749 716 assert_eq!(format!("{:?}", block), "{5: Block(2), 13: Rev(15)}");
750 717 }
751 718
752 719 #[test]
753 720 fn test_raw_block() {
754 721 let mut raw = [255u8; 64];
755 722
756 723 let mut counter = 0;
757 724 for val in [0_i32, 15, -2, -1, -3].iter() {
758 725 for byte in val.to_be_bytes().iter() {
759 726 raw[counter] = *byte;
760 727 counter += 1;
761 728 }
762 729 }
763 730 let (block, _) = Block::from_bytes(&raw).unwrap();
764 731 assert_eq!(block.get(0), Element::Block(0));
765 732 assert_eq!(block.get(1), Element::Block(15));
766 733 assert_eq!(block.get(3), Element::None);
767 734 assert_eq!(block.get(2), Element::Rev(0));
768 735 assert_eq!(block.get(4), Element::Rev(1));
769 736 }
770 737
771 738 type TestIndex = HashMap<Revision, Node>;
772 739
773 740 impl RevlogIndex for TestIndex {
774 741 fn node(&self, rev: Revision) -> Option<&Node> {
775 742 self.get(&rev)
776 743 }
777 744
778 745 fn len(&self) -> usize {
779 746 self.len()
780 747 }
781 748 }
782 749
783 750 /// Pad hexadecimal Node prefix with zeros on the right
784 751 ///
785 752 /// This avoids having to repeatedly write very long hexadecimal
786 753 /// strings for test data, and brings actual hash size independency.
787 754 #[cfg(test)]
788 755 fn pad_node(hex: &str) -> Node {
789 756 Node::from_hex(&hex_pad_right(hex)).unwrap()
790 757 }
791 758
792 759 /// Pad hexadecimal Node prefix with zeros on the right, then insert
793 760 fn pad_insert(idx: &mut TestIndex, rev: Revision, hex: &str) {
794 761 idx.insert(rev, pad_node(hex));
795 762 }
796 763
797 764 fn sample_nodetree() -> NodeTree {
798 765 NodeTree::from(vec![
799 766 block![0: Rev(9)],
800 767 block![0: Rev(0), 1: Rev(9)],
801 768 block![0: Block(1), 1:Rev(1)],
802 769 ])
803 770 }
804 771
772 fn hex(s: &str) -> NodePrefix {
773 NodePrefix::from_hex(s).unwrap()
774 }
775
805 776 #[test]
806 777 fn test_nt_debug() {
807 778 let nt = sample_nodetree();
808 779 assert_eq!(
809 780 format!("{:?}", nt),
810 781 "readonly: \
811 782 [{0: Rev(9)}, {0: Rev(0), 1: Rev(9)}, {0: Block(1), 1: Rev(1)}], \
812 783 growable: [], \
813 784 root: {0: Block(1), 1: Rev(1)}",
814 785 );
815 786 }
816 787
817 788 #[test]
818 789 fn test_immutable_find_simplest() -> Result<(), NodeMapError> {
819 790 let mut idx: TestIndex = HashMap::new();
820 791 pad_insert(&mut idx, 1, "1234deadcafe");
821 792
822 793 let nt = NodeTree::from(vec![block! {1: Rev(1)}]);
823 assert_eq!(nt.find_hex(&idx, "1")?, Some(1));
824 assert_eq!(nt.find_hex(&idx, "12")?, Some(1));
825 assert_eq!(nt.find_hex(&idx, "1234de")?, Some(1));
826 assert_eq!(nt.find_hex(&idx, "1a")?, None);
827 assert_eq!(nt.find_hex(&idx, "ab")?, None);
794 assert_eq!(nt.find_bin(&idx, hex("1"))?, Some(1));
795 assert_eq!(nt.find_bin(&idx, hex("12"))?, Some(1));
796 assert_eq!(nt.find_bin(&idx, hex("1234de"))?, Some(1));
797 assert_eq!(nt.find_bin(&idx, hex("1a"))?, None);
798 assert_eq!(nt.find_bin(&idx, hex("ab"))?, None);
828 799
829 800 // and with full binary Nodes
830 801 assert_eq!(nt.find_node(&idx, idx.get(&1).unwrap())?, Some(1));
831 802 let unknown = Node::from_hex(&hex_pad_right("3d")).unwrap();
832 803 assert_eq!(nt.find_node(&idx, &unknown)?, None);
833 804 Ok(())
834 805 }
835 806
836 807 #[test]
837 808 fn test_immutable_find_one_jump() {
838 809 let mut idx = TestIndex::new();
839 810 pad_insert(&mut idx, 9, "012");
840 811 pad_insert(&mut idx, 0, "00a");
841 812
842 813 let nt = sample_nodetree();
843 814
844 assert_eq!(nt.find_hex(&idx, "0"), Err(MultipleResults));
845 assert_eq!(nt.find_hex(&idx, "01"), Ok(Some(9)));
846 assert_eq!(nt.find_hex(&idx, "00"), Err(MultipleResults));
847 assert_eq!(nt.find_hex(&idx, "00a"), Ok(Some(0)));
848 assert_eq!(nt.unique_prefix_len_hex(&idx, "00a"), Ok(Some(3)));
849 assert_eq!(nt.find_hex(&idx, "000"), Ok(Some(NULL_REVISION)));
815 assert_eq!(nt.find_bin(&idx, hex("0")), Err(MultipleResults));
816 assert_eq!(nt.find_bin(&idx, hex("01")), Ok(Some(9)));
817 assert_eq!(nt.find_bin(&idx, hex("00")), Err(MultipleResults));
818 assert_eq!(nt.find_bin(&idx, hex("00a")), Ok(Some(0)));
819 assert_eq!(nt.unique_prefix_len_bin(&idx, hex("00a")), Ok(Some(3)));
820 assert_eq!(nt.find_bin(&idx, hex("000")), Ok(Some(NULL_REVISION)));
850 821 }
851 822
852 823 #[test]
853 824 fn test_mutated_find() -> Result<(), NodeMapError> {
854 825 let mut idx = TestIndex::new();
855 826 pad_insert(&mut idx, 9, "012");
856 827 pad_insert(&mut idx, 0, "00a");
857 828 pad_insert(&mut idx, 2, "cafe");
858 829 pad_insert(&mut idx, 3, "15");
859 830 pad_insert(&mut idx, 1, "10");
860 831
861 832 let nt = NodeTree {
862 833 readonly: sample_nodetree().readonly,
863 834 growable: vec![block![0: Rev(1), 5: Rev(3)]],
864 835 root: block![0: Block(1), 1:Block(3), 12: Rev(2)],
865 836 masked_inner_blocks: 1,
866 837 };
867 assert_eq!(nt.find_hex(&idx, "10")?, Some(1));
868 assert_eq!(nt.find_hex(&idx, "c")?, Some(2));
869 assert_eq!(nt.unique_prefix_len_hex(&idx, "c")?, Some(1));
870 assert_eq!(nt.find_hex(&idx, "00"), Err(MultipleResults));
871 assert_eq!(nt.find_hex(&idx, "000")?, Some(NULL_REVISION));
872 assert_eq!(nt.unique_prefix_len_hex(&idx, "000")?, Some(3));
873 assert_eq!(nt.find_hex(&idx, "01")?, Some(9));
838 assert_eq!(nt.find_bin(&idx, hex("10"))?, Some(1));
839 assert_eq!(nt.find_bin(&idx, hex("c"))?, Some(2));
840 assert_eq!(nt.unique_prefix_len_bin(&idx, hex("c"))?, Some(1));
841 assert_eq!(nt.find_bin(&idx, hex("00")), Err(MultipleResults));
842 assert_eq!(nt.find_bin(&idx, hex("000"))?, Some(NULL_REVISION));
843 assert_eq!(nt.unique_prefix_len_bin(&idx, hex("000"))?, Some(3));
844 assert_eq!(nt.find_bin(&idx, hex("01"))?, Some(9));
874 845 assert_eq!(nt.masked_readonly_blocks(), 2);
875 846 Ok(())
876 847 }
877 848
878 849 struct TestNtIndex {
879 850 index: TestIndex,
880 851 nt: NodeTree,
881 852 }
882 853
883 854 impl TestNtIndex {
884 855 fn new() -> Self {
885 856 TestNtIndex {
886 857 index: HashMap::new(),
887 858 nt: NodeTree::default(),
888 859 }
889 860 }
890 861
891 862 fn insert(
892 863 &mut self,
893 864 rev: Revision,
894 865 hex: &str,
895 866 ) -> Result<(), NodeMapError> {
896 867 let node = pad_node(hex);
897 868 self.index.insert(rev, node.clone());
898 869 self.nt.insert(&self.index, &node, rev)?;
899 870 Ok(())
900 871 }
901 872
902 873 fn find_hex(
903 874 &self,
904 875 prefix: &str,
905 876 ) -> Result<Option<Revision>, NodeMapError> {
906 self.nt.find_hex(&self.index, prefix)
877 self.nt.find_bin(&self.index, hex(prefix))
907 878 }
908 879
909 880 fn unique_prefix_len_hex(
910 881 &self,
911 882 prefix: &str,
912 883 ) -> Result<Option<usize>, NodeMapError> {
913 self.nt.unique_prefix_len_hex(&self.index, prefix)
884 self.nt.unique_prefix_len_bin(&self.index, hex(prefix))
914 885 }
915 886
916 887 /// Drain `added` and restart a new one
917 888 fn commit(self) -> Self {
918 889 let mut as_vec: Vec<Block> =
919 890 self.nt.readonly.iter().map(|block| block.clone()).collect();
920 891 as_vec.extend(self.nt.growable);
921 892 as_vec.push(self.nt.root);
922 893
923 894 Self {
924 895 index: self.index,
925 896 nt: NodeTree::from(as_vec).into(),
926 897 }
927 898 }
928 899 }
929 900
930 901 #[test]
931 902 fn test_insert_full_mutable() -> Result<(), NodeMapError> {
932 903 let mut idx = TestNtIndex::new();
933 904 idx.insert(0, "1234")?;
934 905 assert_eq!(idx.find_hex("1")?, Some(0));
935 906 assert_eq!(idx.find_hex("12")?, Some(0));
936 907
937 908 // let's trigger a simple split
938 909 idx.insert(1, "1a34")?;
939 910 assert_eq!(idx.nt.growable.len(), 1);
940 911 assert_eq!(idx.find_hex("12")?, Some(0));
941 912 assert_eq!(idx.find_hex("1a")?, Some(1));
942 913
943 914 // reinserting is a no_op
944 915 idx.insert(1, "1a34")?;
945 916 assert_eq!(idx.nt.growable.len(), 1);
946 917 assert_eq!(idx.find_hex("12")?, Some(0));
947 918 assert_eq!(idx.find_hex("1a")?, Some(1));
948 919
949 920 idx.insert(2, "1a01")?;
950 921 assert_eq!(idx.nt.growable.len(), 2);
951 922 assert_eq!(idx.find_hex("1a"), Err(NodeMapError::MultipleResults));
952 923 assert_eq!(idx.find_hex("12")?, Some(0));
953 924 assert_eq!(idx.find_hex("1a3")?, Some(1));
954 925 assert_eq!(idx.find_hex("1a0")?, Some(2));
955 926 assert_eq!(idx.find_hex("1a12")?, None);
956 927
957 928 // now let's make it split and create more than one additional block
958 929 idx.insert(3, "1a345")?;
959 930 assert_eq!(idx.nt.growable.len(), 4);
960 931 assert_eq!(idx.find_hex("1a340")?, Some(1));
961 932 assert_eq!(idx.find_hex("1a345")?, Some(3));
962 933 assert_eq!(idx.find_hex("1a341")?, None);
963 934
964 935 // there's no readonly block to mask
965 936 assert_eq!(idx.nt.masked_readonly_blocks(), 0);
966 937 Ok(())
967 938 }
968 939
969 940 #[test]
970 941 fn test_unique_prefix_len_zero_prefix() {
971 942 let mut idx = TestNtIndex::new();
972 943 idx.insert(0, "00000abcd").unwrap();
973 944
974 945 assert_eq!(idx.find_hex("000"), Err(NodeMapError::MultipleResults));
975 946 // in the nodetree proper, this will be found at the first nybble
976 947 // yet the correct answer for unique_prefix_len is not 1, nor 1+1,
977 948 // but the first difference with `NULL_NODE`
978 949 assert_eq!(idx.unique_prefix_len_hex("00000a"), Ok(Some(6)));
979 950 assert_eq!(idx.unique_prefix_len_hex("00000ab"), Ok(Some(6)));
980 951
981 952 // same with odd result
982 953 idx.insert(1, "00123").unwrap();
983 954 assert_eq!(idx.unique_prefix_len_hex("001"), Ok(Some(3)));
984 955 assert_eq!(idx.unique_prefix_len_hex("0012"), Ok(Some(3)));
985 956
986 957 // these are unchanged of course
987 958 assert_eq!(idx.unique_prefix_len_hex("00000a"), Ok(Some(6)));
988 959 assert_eq!(idx.unique_prefix_len_hex("00000ab"), Ok(Some(6)));
989 960 }
990 961
991 962 #[test]
992 963 fn test_insert_extreme_splitting() -> Result<(), NodeMapError> {
993 964 // check that the splitting loop is long enough
994 965 let mut nt_idx = TestNtIndex::new();
995 966 let nt = &mut nt_idx.nt;
996 967 let idx = &mut nt_idx.index;
997 968
998 969 let node0_hex = hex_pad_right("444444");
999 970 let mut node1_hex = hex_pad_right("444444").clone();
1000 971 node1_hex.pop();
1001 972 node1_hex.push('5');
1002 973 let node0 = Node::from_hex(&node0_hex).unwrap();
1003 974 let node1 = Node::from_hex(&node1_hex).unwrap();
1004 975
1005 976 idx.insert(0, node0.clone());
1006 977 nt.insert(idx, &node0, 0)?;
1007 978 idx.insert(1, node1.clone());
1008 979 nt.insert(idx, &node1, 1)?;
1009 980
1010 981 assert_eq!(nt.find_bin(idx, (&node0).into())?, Some(0));
1011 982 assert_eq!(nt.find_bin(idx, (&node1).into())?, Some(1));
1012 983 Ok(())
1013 984 }
1014 985
1015 986 #[test]
1016 987 fn test_insert_partly_immutable() -> Result<(), NodeMapError> {
1017 988 let mut idx = TestNtIndex::new();
1018 989 idx.insert(0, "1234")?;
1019 990 idx.insert(1, "1235")?;
1020 991 idx.insert(2, "131")?;
1021 992 idx.insert(3, "cafe")?;
1022 993 let mut idx = idx.commit();
1023 994 assert_eq!(idx.find_hex("1234")?, Some(0));
1024 995 assert_eq!(idx.find_hex("1235")?, Some(1));
1025 996 assert_eq!(idx.find_hex("131")?, Some(2));
1026 997 assert_eq!(idx.find_hex("cafe")?, Some(3));
1027 998 // we did not add anything since init from readonly
1028 999 assert_eq!(idx.nt.masked_readonly_blocks(), 0);
1029 1000
1030 1001 idx.insert(4, "123A")?;
1031 1002 assert_eq!(idx.find_hex("1234")?, Some(0));
1032 1003 assert_eq!(idx.find_hex("1235")?, Some(1));
1033 1004 assert_eq!(idx.find_hex("131")?, Some(2));
1034 1005 assert_eq!(idx.find_hex("cafe")?, Some(3));
1035 1006 assert_eq!(idx.find_hex("123A")?, Some(4));
1036 1007 // we masked blocks for all prefixes of "123", including the root
1037 1008 assert_eq!(idx.nt.masked_readonly_blocks(), 4);
1038 1009
1039 1010 eprintln!("{:?}", idx.nt);
1040 1011 idx.insert(5, "c0")?;
1041 1012 assert_eq!(idx.find_hex("cafe")?, Some(3));
1042 1013 assert_eq!(idx.find_hex("c0")?, Some(5));
1043 1014 assert_eq!(idx.find_hex("c1")?, None);
1044 1015 assert_eq!(idx.find_hex("1234")?, Some(0));
1045 1016 // inserting "c0" is just splitting the 'c' slot of the mutable root,
1046 1017 // it doesn't mask anything
1047 1018 assert_eq!(idx.nt.masked_readonly_blocks(), 4);
1048 1019
1049 1020 Ok(())
1050 1021 }
1051 1022
1052 1023 #[test]
1053 1024 fn test_invalidate_all() -> Result<(), NodeMapError> {
1054 1025 let mut idx = TestNtIndex::new();
1055 1026 idx.insert(0, "1234")?;
1056 1027 idx.insert(1, "1235")?;
1057 1028 idx.insert(2, "131")?;
1058 1029 idx.insert(3, "cafe")?;
1059 1030 let mut idx = idx.commit();
1060 1031
1061 1032 idx.nt.invalidate_all();
1062 1033
1063 1034 assert_eq!(idx.find_hex("1234")?, None);
1064 1035 assert_eq!(idx.find_hex("1235")?, None);
1065 1036 assert_eq!(idx.find_hex("131")?, None);
1066 1037 assert_eq!(idx.find_hex("cafe")?, None);
1067 1038 // all the readonly blocks have been masked, this is the
1068 1039 // conventional expected response
1069 1040 assert_eq!(idx.nt.masked_readonly_blocks(), idx.nt.readonly.len() + 1);
1070 1041 Ok(())
1071 1042 }
1072 1043
1073 1044 #[test]
1074 1045 fn test_into_added_empty() {
1075 1046 assert!(sample_nodetree().into_readonly_and_added().1.is_empty());
1076 1047 assert!(sample_nodetree()
1077 1048 .into_readonly_and_added_bytes()
1078 1049 .1
1079 1050 .is_empty());
1080 1051 }
1081 1052
1082 1053 #[test]
1083 1054 fn test_into_added_bytes() -> Result<(), NodeMapError> {
1084 1055 let mut idx = TestNtIndex::new();
1085 1056 idx.insert(0, "1234")?;
1086 1057 let mut idx = idx.commit();
1087 1058 idx.insert(4, "cafe")?;
1088 1059 let (_, bytes) = idx.nt.into_readonly_and_added_bytes();
1089 1060
1090 1061 // only the root block has been changed
1091 1062 assert_eq!(bytes.len(), size_of::<Block>());
1092 1063 // big endian for -2
1093 1064 assert_eq!(&bytes[4..2 * 4], [255, 255, 255, 254]);
1094 1065 // big endian for -6
1095 1066 assert_eq!(&bytes[12 * 4..13 * 4], [255, 255, 255, 250]);
1096 1067 Ok(())
1097 1068 }
1098 1069 }
@@ -1,491 +1,490
1 1 // revlog.rs
2 2 //
3 3 // Copyright 2019-2020 Georges Racinet <georges.racinet@octobus.net>
4 4 //
5 5 // This software may be used and distributed according to the terms of the
6 6 // GNU General Public License version 2 or any later version.
7 7
8 8 use crate::{
9 9 cindex,
10 10 utils::{node_from_py_bytes, node_from_py_object},
11 11 };
12 12 use cpython::{
13 13 buffer::{Element, PyBuffer},
14 14 exc::{IndexError, ValueError},
15 15 ObjectProtocol, PyBytes, PyClone, PyDict, PyErr, PyModule, PyObject,
16 16 PyResult, PyString, PyTuple, Python, PythonObject, ToPyObject,
17 17 };
18 18 use hg::{
19 19 nodemap::{Block, NodeMapError, NodeTree},
20 revlog::{nodemap::NodeMap, RevlogIndex},
20 revlog::{nodemap::NodeMap, NodePrefix, RevlogIndex},
21 21 Revision,
22 22 };
23 23 use std::cell::RefCell;
24 24
25 25 /// Return a Struct implementing the Graph trait
26 26 pub(crate) fn pyindex_to_graph(
27 27 py: Python,
28 28 index: PyObject,
29 29 ) -> PyResult<cindex::Index> {
30 30 match index.extract::<MixedIndex>(py) {
31 31 Ok(midx) => Ok(midx.clone_cindex(py)),
32 32 Err(_) => cindex::Index::new(py, index),
33 33 }
34 34 }
35 35
36 36 py_class!(pub class MixedIndex |py| {
37 37 data cindex: RefCell<cindex::Index>;
38 38 data nt: RefCell<Option<NodeTree>>;
39 39 data docket: RefCell<Option<PyObject>>;
40 40 // Holds a reference to the mmap'ed persistent nodemap data
41 41 data mmap: RefCell<Option<PyBuffer>>;
42 42
43 43 def __new__(_cls, cindex: PyObject) -> PyResult<MixedIndex> {
44 44 Self::new(py, cindex)
45 45 }
46 46
47 47 /// Compatibility layer used for Python consumers needing access to the C index
48 48 ///
49 49 /// Only use case so far is `scmutil.shortesthexnodeidprefix`,
50 50 /// that may need to build a custom `nodetree`, based on a specified revset.
51 51 /// With a Rust implementation of the nodemap, we will be able to get rid of
52 52 /// this, by exposing our own standalone nodemap class,
53 53 /// ready to accept `MixedIndex`.
54 54 def get_cindex(&self) -> PyResult<PyObject> {
55 55 Ok(self.cindex(py).borrow().inner().clone_ref(py))
56 56 }
57 57
58 58 // Index API involving nodemap, as defined in mercurial/pure/parsers.py
59 59
60 60 /// Return Revision if found, raises a bare `error.RevlogError`
61 61 /// in case of ambiguity, same as C version does
62 62 def get_rev(&self, node: PyBytes) -> PyResult<Option<Revision>> {
63 63 let opt = self.get_nodetree(py)?.borrow();
64 64 let nt = opt.as_ref().unwrap();
65 65 let idx = &*self.cindex(py).borrow();
66 66 let node = node_from_py_bytes(py, &node)?;
67 67 nt.find_bin(idx, node.into()).map_err(|e| nodemap_error(py, e))
68 68 }
69 69
70 70 /// same as `get_rev()` but raises a bare `error.RevlogError` if node
71 71 /// is not found.
72 72 ///
73 73 /// No need to repeat `node` in the exception, `mercurial/revlog.py`
74 74 /// will catch and rewrap with it
75 75 def rev(&self, node: PyBytes) -> PyResult<Revision> {
76 76 self.get_rev(py, node)?.ok_or_else(|| revlog_error(py))
77 77 }
78 78
79 79 /// return True if the node exist in the index
80 80 def has_node(&self, node: PyBytes) -> PyResult<bool> {
81 81 self.get_rev(py, node).map(|opt| opt.is_some())
82 82 }
83 83
84 84 /// find length of shortest hex nodeid of a binary ID
85 85 def shortest(&self, node: PyBytes) -> PyResult<usize> {
86 86 let opt = self.get_nodetree(py)?.borrow();
87 87 let nt = opt.as_ref().unwrap();
88 88 let idx = &*self.cindex(py).borrow();
89 89 match nt.unique_prefix_len_node(idx, &node_from_py_bytes(py, &node)?)
90 90 {
91 91 Ok(Some(l)) => Ok(l),
92 92 Ok(None) => Err(revlog_error(py)),
93 93 Err(e) => Err(nodemap_error(py, e)),
94 94 }
95 95 }
96 96
97 97 def partialmatch(&self, node: PyObject) -> PyResult<Option<PyBytes>> {
98 98 let opt = self.get_nodetree(py)?.borrow();
99 99 let nt = opt.as_ref().unwrap();
100 100 let idx = &*self.cindex(py).borrow();
101 101
102 102 let node_as_string = if cfg!(feature = "python3-sys") {
103 103 node.cast_as::<PyString>(py)?.to_string(py)?.to_string()
104 104 }
105 105 else {
106 106 let node = node.extract::<PyBytes>(py)?;
107 107 String::from_utf8_lossy(node.data(py)).to_string()
108 108 };
109 109
110 nt.find_hex(idx, &node_as_string)
110 let prefix = NodePrefix::from_hex(&node_as_string).map_err(|_| PyErr::new::<ValueError, _>(py, "Invalid node or prefix"))?;
111
112 nt.find_bin(idx, prefix)
111 113 // TODO make an inner API returning the node directly
112 114 .map(|opt| opt.map(
113 115 |rev| PyBytes::new(py, idx.node(rev).unwrap().as_bytes())))
114 116 .map_err(|e| nodemap_error(py, e))
115 117
116 118 }
117 119
118 120 /// append an index entry
119 121 def append(&self, tup: PyTuple) -> PyResult<PyObject> {
120 122 if tup.len(py) < 8 {
121 123 // this is better than the panic promised by tup.get_item()
122 124 return Err(
123 125 PyErr::new::<IndexError, _>(py, "tuple index out of range"))
124 126 }
125 127 let node_bytes = tup.get_item(py, 7).extract(py)?;
126 128 let node = node_from_py_object(py, &node_bytes)?;
127 129
128 130 let mut idx = self.cindex(py).borrow_mut();
129 131 let rev = idx.len() as Revision;
130 132
131 133 idx.append(py, tup)?;
132 134 self.get_nodetree(py)?.borrow_mut().as_mut().unwrap()
133 135 .insert(&*idx, &node, rev)
134 136 .map_err(|e| nodemap_error(py, e))?;
135 137 Ok(py.None())
136 138 }
137 139
138 140 def __delitem__(&self, key: PyObject) -> PyResult<()> {
139 141 // __delitem__ is both for `del idx[r]` and `del idx[r1:r2]`
140 142 self.cindex(py).borrow().inner().del_item(py, key)?;
141 143 let mut opt = self.get_nodetree(py)?.borrow_mut();
142 144 let mut nt = opt.as_mut().unwrap();
143 145 nt.invalidate_all();
144 146 self.fill_nodemap(py, &mut nt)?;
145 147 Ok(())
146 148 }
147 149
148 150 //
149 151 // Reforwarded C index API
150 152 //
151 153
152 154 // index_methods (tp_methods). Same ordering as in revlog.c
153 155
154 156 /// return the gca set of the given revs
155 157 def ancestors(&self, *args, **kw) -> PyResult<PyObject> {
156 158 self.call_cindex(py, "ancestors", args, kw)
157 159 }
158 160
159 161 /// return the heads of the common ancestors of the given revs
160 162 def commonancestorsheads(&self, *args, **kw) -> PyResult<PyObject> {
161 163 self.call_cindex(py, "commonancestorsheads", args, kw)
162 164 }
163 165
164 166 /// Clear the index caches and inner py_class data.
165 167 /// It is Python's responsibility to call `update_nodemap_data` again.
166 168 def clearcaches(&self, *args, **kw) -> PyResult<PyObject> {
167 169 self.nt(py).borrow_mut().take();
168 170 self.docket(py).borrow_mut().take();
169 171 self.mmap(py).borrow_mut().take();
170 172 self.call_cindex(py, "clearcaches", args, kw)
171 173 }
172 174
173 175 /// get an index entry
174 176 def get(&self, *args, **kw) -> PyResult<PyObject> {
175 177 self.call_cindex(py, "get", args, kw)
176 178 }
177 179
178 180 /// compute phases
179 181 def computephasesmapsets(&self, *args, **kw) -> PyResult<PyObject> {
180 182 self.call_cindex(py, "computephasesmapsets", args, kw)
181 183 }
182 184
183 185 /// reachableroots
184 186 def reachableroots2(&self, *args, **kw) -> PyResult<PyObject> {
185 187 self.call_cindex(py, "reachableroots2", args, kw)
186 188 }
187 189
188 190 /// get head revisions
189 191 def headrevs(&self, *args, **kw) -> PyResult<PyObject> {
190 192 self.call_cindex(py, "headrevs", args, kw)
191 193 }
192 194
193 195 /// get filtered head revisions
194 196 def headrevsfiltered(&self, *args, **kw) -> PyResult<PyObject> {
195 197 self.call_cindex(py, "headrevsfiltered", args, kw)
196 198 }
197 199
198 200 /// True if the object is a snapshot
199 201 def issnapshot(&self, *args, **kw) -> PyResult<PyObject> {
200 202 self.call_cindex(py, "issnapshot", args, kw)
201 203 }
202 204
203 205 /// Gather snapshot data in a cache dict
204 206 def findsnapshots(&self, *args, **kw) -> PyResult<PyObject> {
205 207 self.call_cindex(py, "findsnapshots", args, kw)
206 208 }
207 209
208 210 /// determine revisions with deltas to reconstruct fulltext
209 211 def deltachain(&self, *args, **kw) -> PyResult<PyObject> {
210 212 self.call_cindex(py, "deltachain", args, kw)
211 213 }
212 214
213 215 /// slice planned chunk read to reach a density threshold
214 216 def slicechunktodensity(&self, *args, **kw) -> PyResult<PyObject> {
215 217 self.call_cindex(py, "slicechunktodensity", args, kw)
216 218 }
217 219
218 220 /// stats for the index
219 221 def stats(&self, *args, **kw) -> PyResult<PyObject> {
220 222 self.call_cindex(py, "stats", args, kw)
221 223 }
222 224
223 225 // index_sequence_methods and index_mapping_methods.
224 226 //
225 227 // Since we call back through the high level Python API,
226 228 // there's no point making a distinction between index_get
227 229 // and index_getitem.
228 230
229 231 def __len__(&self) -> PyResult<usize> {
230 232 self.cindex(py).borrow().inner().len(py)
231 233 }
232 234
233 235 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
234 236 // this conversion seems needless, but that's actually because
235 237 // `index_getitem` does not handle conversion from PyLong,
236 238 // which expressions such as [e for e in index] internally use.
237 239 // Note that we don't seem to have a direct way to call
238 240 // PySequence_GetItem (does the job), which would possibly be better
239 241 // for performance
240 242 let key = match key.extract::<Revision>(py) {
241 243 Ok(rev) => rev.to_py_object(py).into_object(),
242 244 Err(_) => key,
243 245 };
244 246 self.cindex(py).borrow().inner().get_item(py, key)
245 247 }
246 248
247 249 def __setitem__(&self, key: PyObject, value: PyObject) -> PyResult<()> {
248 250 self.cindex(py).borrow().inner().set_item(py, key, value)
249 251 }
250 252
251 253 def __contains__(&self, item: PyObject) -> PyResult<bool> {
252 254 // ObjectProtocol does not seem to provide contains(), so
253 255 // this is an equivalent implementation of the index_contains()
254 256 // defined in revlog.c
255 257 let cindex = self.cindex(py).borrow();
256 258 match item.extract::<Revision>(py) {
257 259 Ok(rev) => {
258 260 Ok(rev >= -1 && rev < cindex.inner().len(py)? as Revision)
259 261 }
260 262 Err(_) => {
261 263 cindex.inner().call_method(
262 264 py,
263 265 "has_node",
264 266 PyTuple::new(py, &[item]),
265 267 None)?
266 268 .extract(py)
267 269 }
268 270 }
269 271 }
270 272
271 273 def nodemap_data_all(&self) -> PyResult<PyBytes> {
272 274 self.inner_nodemap_data_all(py)
273 275 }
274 276
275 277 def nodemap_data_incremental(&self) -> PyResult<PyObject> {
276 278 self.inner_nodemap_data_incremental(py)
277 279 }
278 280 def update_nodemap_data(
279 281 &self,
280 282 docket: PyObject,
281 283 nm_data: PyObject
282 284 ) -> PyResult<PyObject> {
283 285 self.inner_update_nodemap_data(py, docket, nm_data)
284 286 }
285 287
286 288
287 289 });
288 290
289 291 impl MixedIndex {
290 292 fn new(py: Python, cindex: PyObject) -> PyResult<MixedIndex> {
291 293 Self::create_instance(
292 294 py,
293 295 RefCell::new(cindex::Index::new(py, cindex)?),
294 296 RefCell::new(None),
295 297 RefCell::new(None),
296 298 RefCell::new(None),
297 299 )
298 300 }
299 301
300 302 /// This is scaffolding at this point, but it could also become
301 303 /// a way to start a persistent nodemap or perform a
302 304 /// vacuum / repack operation
303 305 fn fill_nodemap(
304 306 &self,
305 307 py: Python,
306 308 nt: &mut NodeTree,
307 309 ) -> PyResult<PyObject> {
308 310 let index = self.cindex(py).borrow();
309 311 for r in 0..index.len() {
310 312 let rev = r as Revision;
311 313 // in this case node() won't ever return None
312 314 nt.insert(&*index, index.node(rev).unwrap(), rev)
313 315 .map_err(|e| nodemap_error(py, e))?
314 316 }
315 317 Ok(py.None())
316 318 }
317 319
318 320 fn get_nodetree<'a>(
319 321 &'a self,
320 322 py: Python<'a>,
321 323 ) -> PyResult<&'a RefCell<Option<NodeTree>>> {
322 324 if self.nt(py).borrow().is_none() {
323 325 let readonly = Box::new(Vec::new());
324 326 let mut nt = NodeTree::load_bytes(readonly, 0);
325 327 self.fill_nodemap(py, &mut nt)?;
326 328 self.nt(py).borrow_mut().replace(nt);
327 329 }
328 330 Ok(self.nt(py))
329 331 }
330 332
331 333 /// forward a method call to the underlying C index
332 334 fn call_cindex(
333 335 &self,
334 336 py: Python,
335 337 name: &str,
336 338 args: &PyTuple,
337 339 kwargs: Option<&PyDict>,
338 340 ) -> PyResult<PyObject> {
339 341 self.cindex(py)
340 342 .borrow()
341 343 .inner()
342 344 .call_method(py, name, args, kwargs)
343 345 }
344 346
345 347 pub fn clone_cindex(&self, py: Python) -> cindex::Index {
346 348 self.cindex(py).borrow().clone_ref(py)
347 349 }
348 350
349 351 /// Returns the full nodemap bytes to be written as-is to disk
350 352 fn inner_nodemap_data_all(&self, py: Python) -> PyResult<PyBytes> {
351 353 let nodemap = self.get_nodetree(py)?.borrow_mut().take().unwrap();
352 354 let (readonly, bytes) = nodemap.into_readonly_and_added_bytes();
353 355
354 356 // If there's anything readonly, we need to build the data again from
355 357 // scratch
356 358 let bytes = if readonly.len() > 0 {
357 359 let mut nt = NodeTree::load_bytes(Box::new(vec![]), 0);
358 360 self.fill_nodemap(py, &mut nt)?;
359 361
360 362 let (readonly, bytes) = nt.into_readonly_and_added_bytes();
361 363 assert_eq!(readonly.len(), 0);
362 364
363 365 bytes
364 366 } else {
365 367 bytes
366 368 };
367 369
368 370 let bytes = PyBytes::new(py, &bytes);
369 371 Ok(bytes)
370 372 }
371 373
372 374 /// Returns the last saved docket along with the size of any changed data
373 375 /// (in number of blocks), and said data as bytes.
374 376 fn inner_nodemap_data_incremental(
375 377 &self,
376 378 py: Python,
377 379 ) -> PyResult<PyObject> {
378 380 let docket = self.docket(py).borrow();
379 381 let docket = match docket.as_ref() {
380 382 Some(d) => d,
381 383 None => return Ok(py.None()),
382 384 };
383 385
384 386 let node_tree = self.get_nodetree(py)?.borrow_mut().take().unwrap();
385 387 let masked_blocks = node_tree.masked_readonly_blocks();
386 388 let (_, data) = node_tree.into_readonly_and_added_bytes();
387 389 let changed = masked_blocks * std::mem::size_of::<Block>();
388 390
389 391 Ok((docket, changed, PyBytes::new(py, &data))
390 392 .to_py_object(py)
391 393 .into_object())
392 394 }
393 395
394 396 /// Update the nodemap from the new (mmaped) data.
395 397 /// The docket is kept as a reference for later incremental calls.
396 398 fn inner_update_nodemap_data(
397 399 &self,
398 400 py: Python,
399 401 docket: PyObject,
400 402 nm_data: PyObject,
401 403 ) -> PyResult<PyObject> {
402 404 let buf = PyBuffer::get(py, &nm_data)?;
403 405 let len = buf.item_count();
404 406
405 407 // Build a slice from the mmap'ed buffer data
406 408 let cbuf = buf.buf_ptr();
407 409 let bytes = if std::mem::size_of::<u8>() == buf.item_size()
408 410 && buf.is_c_contiguous()
409 411 && u8::is_compatible_format(buf.format())
410 412 {
411 413 unsafe { std::slice::from_raw_parts(cbuf as *const u8, len) }
412 414 } else {
413 415 return Err(PyErr::new::<ValueError, _>(
414 416 py,
415 417 "Nodemap data buffer has an invalid memory representation"
416 418 .to_string(),
417 419 ));
418 420 };
419 421
420 422 // Keep a reference to the mmap'ed buffer, otherwise we get a dangling
421 423 // pointer.
422 424 self.mmap(py).borrow_mut().replace(buf);
423 425
424 426 let mut nt = NodeTree::load_bytes(Box::new(bytes), len);
425 427
426 428 let data_tip =
427 429 docket.getattr(py, "tip_rev")?.extract::<Revision>(py)?;
428 430 self.docket(py).borrow_mut().replace(docket.clone_ref(py));
429 431 let idx = self.cindex(py).borrow();
430 432 let current_tip = idx.len();
431 433
432 434 for r in (data_tip + 1)..current_tip as Revision {
433 435 let rev = r as Revision;
434 436 // in this case node() won't ever return None
435 437 nt.insert(&*idx, idx.node(rev).unwrap(), rev)
436 438 .map_err(|e| nodemap_error(py, e))?
437 439 }
438 440
439 441 *self.nt(py).borrow_mut() = Some(nt);
440 442
441 443 Ok(py.None())
442 444 }
443 445 }
444 446
445 447 fn revlog_error(py: Python) -> PyErr {
446 448 match py
447 449 .import("mercurial.error")
448 450 .and_then(|m| m.get(py, "RevlogError"))
449 451 {
450 452 Err(e) => e,
451 453 Ok(cls) => PyErr::from_instance(py, cls),
452 454 }
453 455 }
454 456
455 457 fn rev_not_in_index(py: Python, rev: Revision) -> PyErr {
456 458 PyErr::new::<ValueError, _>(
457 459 py,
458 460 format!(
459 461 "Inconsistency: Revision {} found in nodemap \
460 462 is not in revlog index",
461 463 rev
462 464 ),
463 465 )
464 466 }
465 467
466 468 /// Standard treatment of NodeMapError
467 469 fn nodemap_error(py: Python, err: NodeMapError) -> PyErr {
468 470 match err {
469 471 NodeMapError::MultipleResults => revlog_error(py),
470 472 NodeMapError::RevisionNotInIndex(r) => rev_not_in_index(py, r),
471 NodeMapError::InvalidNodePrefix => {
472 PyErr::new::<ValueError, _>(py, "Invalid node or prefix")
473 }
474 473 }
475 474 }
476 475
477 476 /// Create the module, with __package__ given from parent
478 477 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
479 478 let dotted_name = &format!("{}.revlog", package);
480 479 let m = PyModule::new(py, dotted_name)?;
481 480 m.add(py, "__package__", package)?;
482 481 m.add(py, "__doc__", "RevLog - Rust implementations")?;
483 482
484 483 m.add_class::<MixedIndex>(py)?;
485 484
486 485 let sys = PyModule::import(py, "sys")?;
487 486 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
488 487 sys_modules.set_item(py, dotted_name, &m)?;
489 488
490 489 Ok(m)
491 490 }
General Comments 0
You need to be logged in to leave comments. Login now