annotate rust/hg-core/src/revlog/nodemap_docket.rs @ 47347:73ddcedeaadf

dirstate-tree: Change status() results to not borrow DirstateMap The `status` function takes a `&'tree mut DirstateMap<'on_disk>` parameter. `'on_disk` borrows a read-only byte buffer with the contents of the `.hg/dirstate` file. `DirstateMap` internally uses represents file paths as `std::borrow::Cow<'on_disk, HgPath>`, which borrows the byte buffer when possible and allocates an owned string if not, such as for files added to the dirstate after it was loaded from disk. Previously the return type of of `status` has a `'tree`?lifetime, meaning it could borrow all paths from the `DirstateMap`. With this changeset, that lifetime is changed to `'on_disk` meaning that only paths from the byte buffer can be borrowed, and paths allocated by `DirstateMap` must be copied. Usually most paths are in the byte buffer, and most paths are not part of the return value of `status`, so the number of extra copies should be small. This change will enable `status` to mutate the `DirstateMap` after it has finished constructing its return value. Previously such mutation would be prevented by possible on-going borrows. Differential Revision: https://phab.mercurial-scm.org/D10824
author Simon Sapin <simon.sapin@octobus.net>
date Fri, 28 May 2021 20:07:27 +0200
parents 842f2372ced6
children e834b79def74
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
46443
43d63979a75e rust: use HgError in RevlogError and Vfs
Simon Sapin <simon.sapin@octobus.net>
parents: 46390
diff changeset
1 use crate::errors::{HgError, HgResultExt};
46630
842f2372ced6 rhg: Don?t attempt to read persistent nodemap without .hg/requires opt-in
Simon Sapin <simon.sapin@octobus.net>
parents: 46443
diff changeset
2 use crate::requirements;
46390
0800aa42bb4c rust: use the bytes-cast crate to parse persistent nodemaps
Simon Sapin <simon.sapin@octobus.net>
parents: 46167
diff changeset
3 use bytes_cast::{unaligned, BytesCast};
46090
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
4 use memmap::Mmap;
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
5 use std::path::{Path, PathBuf};
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
6
46167
8a4914397d02 rust: introduce Repo and Vfs types for filesystem abstraction
Simon Sapin <simon.sapin@octobus.net>
parents: 46090
diff changeset
7 use super::revlog::RevlogError;
8a4914397d02 rust: introduce Repo and Vfs types for filesystem abstraction
Simon Sapin <simon.sapin@octobus.net>
parents: 46090
diff changeset
8 use crate::repo::Repo;
46090
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
9 use crate::utils::strip_suffix;
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
10
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
11 const ONDISK_VERSION: u8 = 1;
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
12
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
13 pub(super) struct NodeMapDocket {
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
14 pub data_length: usize,
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
15 // TODO: keep here more of the data from `parse()` when we need it
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
16 }
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
17
46390
0800aa42bb4c rust: use the bytes-cast crate to parse persistent nodemaps
Simon Sapin <simon.sapin@octobus.net>
parents: 46167
diff changeset
18 #[derive(BytesCast)]
0800aa42bb4c rust: use the bytes-cast crate to parse persistent nodemaps
Simon Sapin <simon.sapin@octobus.net>
parents: 46167
diff changeset
19 #[repr(C)]
0800aa42bb4c rust: use the bytes-cast crate to parse persistent nodemaps
Simon Sapin <simon.sapin@octobus.net>
parents: 46167
diff changeset
20 struct DocketHeader {
0800aa42bb4c rust: use the bytes-cast crate to parse persistent nodemaps
Simon Sapin <simon.sapin@octobus.net>
parents: 46167
diff changeset
21 uid_size: u8,
0800aa42bb4c rust: use the bytes-cast crate to parse persistent nodemaps
Simon Sapin <simon.sapin@octobus.net>
parents: 46167
diff changeset
22 _tip_rev: unaligned::U64Be,
0800aa42bb4c rust: use the bytes-cast crate to parse persistent nodemaps
Simon Sapin <simon.sapin@octobus.net>
parents: 46167
diff changeset
23 data_length: unaligned::U64Be,
0800aa42bb4c rust: use the bytes-cast crate to parse persistent nodemaps
Simon Sapin <simon.sapin@octobus.net>
parents: 46167
diff changeset
24 _data_unused: unaligned::U64Be,
0800aa42bb4c rust: use the bytes-cast crate to parse persistent nodemaps
Simon Sapin <simon.sapin@octobus.net>
parents: 46167
diff changeset
25 tip_node_size: unaligned::U64Be,
0800aa42bb4c rust: use the bytes-cast crate to parse persistent nodemaps
Simon Sapin <simon.sapin@octobus.net>
parents: 46167
diff changeset
26 }
0800aa42bb4c rust: use the bytes-cast crate to parse persistent nodemaps
Simon Sapin <simon.sapin@octobus.net>
parents: 46167
diff changeset
27
46090
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
28 impl NodeMapDocket {
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
29 /// Return `Ok(None)` when the caller should proceed without a persistent
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
30 /// nodemap:
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
31 ///
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
32 /// * This revlog does not have a `.n` docket file (it is not generated for
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
33 /// small revlogs), or
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
34 /// * The docket has an unsupported version number (repositories created by
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
35 /// later hg, maybe that should be a requirement instead?), or
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
36 /// * The docket file points to a missing (likely deleted) data file (this
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
37 /// can happen in a rare race condition).
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
38 pub fn read_from_file(
46167
8a4914397d02 rust: introduce Repo and Vfs types for filesystem abstraction
Simon Sapin <simon.sapin@octobus.net>
parents: 46090
diff changeset
39 repo: &Repo,
46090
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
40 index_path: &Path,
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
41 ) -> Result<Option<(Self, Mmap)>, RevlogError> {
46630
842f2372ced6 rhg: Don?t attempt to read persistent nodemap without .hg/requires opt-in
Simon Sapin <simon.sapin@octobus.net>
parents: 46443
diff changeset
42 if !repo
842f2372ced6 rhg: Don?t attempt to read persistent nodemap without .hg/requires opt-in
Simon Sapin <simon.sapin@octobus.net>
parents: 46443
diff changeset
43 .requirements()
842f2372ced6 rhg: Don?t attempt to read persistent nodemap without .hg/requires opt-in
Simon Sapin <simon.sapin@octobus.net>
parents: 46443
diff changeset
44 .contains(requirements::NODEMAP_REQUIREMENT)
842f2372ced6 rhg: Don?t attempt to read persistent nodemap without .hg/requires opt-in
Simon Sapin <simon.sapin@octobus.net>
parents: 46443
diff changeset
45 {
842f2372ced6 rhg: Don?t attempt to read persistent nodemap without .hg/requires opt-in
Simon Sapin <simon.sapin@octobus.net>
parents: 46443
diff changeset
46 // If .hg/requires does not opt it, don’t try to open a nodemap
842f2372ced6 rhg: Don?t attempt to read persistent nodemap without .hg/requires opt-in
Simon Sapin <simon.sapin@octobus.net>
parents: 46443
diff changeset
47 return Ok(None);
842f2372ced6 rhg: Don?t attempt to read persistent nodemap without .hg/requires opt-in
Simon Sapin <simon.sapin@octobus.net>
parents: 46443
diff changeset
48 }
842f2372ced6 rhg: Don?t attempt to read persistent nodemap without .hg/requires opt-in
Simon Sapin <simon.sapin@octobus.net>
parents: 46443
diff changeset
49
46090
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
50 let docket_path = index_path.with_extension("n");
46443
43d63979a75e rust: use HgError in RevlogError and Vfs
Simon Sapin <simon.sapin@octobus.net>
parents: 46390
diff changeset
51 let docket_bytes = if let Some(bytes) =
43d63979a75e rust: use HgError in RevlogError and Vfs
Simon Sapin <simon.sapin@octobus.net>
parents: 46390
diff changeset
52 repo.store_vfs().read(&docket_path).io_not_found_as_none()?
43d63979a75e rust: use HgError in RevlogError and Vfs
Simon Sapin <simon.sapin@octobus.net>
parents: 46390
diff changeset
53 {
43d63979a75e rust: use HgError in RevlogError and Vfs
Simon Sapin <simon.sapin@octobus.net>
parents: 46390
diff changeset
54 bytes
43d63979a75e rust: use HgError in RevlogError and Vfs
Simon Sapin <simon.sapin@octobus.net>
parents: 46390
diff changeset
55 } else {
43d63979a75e rust: use HgError in RevlogError and Vfs
Simon Sapin <simon.sapin@octobus.net>
parents: 46390
diff changeset
56 return Ok(None);
46090
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
57 };
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
58
46390
0800aa42bb4c rust: use the bytes-cast crate to parse persistent nodemaps
Simon Sapin <simon.sapin@octobus.net>
parents: 46167
diff changeset
59 let input = if let Some((&ONDISK_VERSION, rest)) =
46090
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
60 docket_bytes.split_first()
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
61 {
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
62 rest
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
63 } else {
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
64 return Ok(None);
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
65 };
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
66
46443
43d63979a75e rust: use HgError in RevlogError and Vfs
Simon Sapin <simon.sapin@octobus.net>
parents: 46390
diff changeset
67 /// Treat any error as a parse error
43d63979a75e rust: use HgError in RevlogError and Vfs
Simon Sapin <simon.sapin@octobus.net>
parents: 46390
diff changeset
68 fn parse<T, E>(result: Result<T, E>) -> Result<T, RevlogError> {
43d63979a75e rust: use HgError in RevlogError and Vfs
Simon Sapin <simon.sapin@octobus.net>
parents: 46390
diff changeset
69 result.map_err(|_| {
43d63979a75e rust: use HgError in RevlogError and Vfs
Simon Sapin <simon.sapin@octobus.net>
parents: 46390
diff changeset
70 HgError::corrupted("nodemap docket parse error").into()
43d63979a75e rust: use HgError in RevlogError and Vfs
Simon Sapin <simon.sapin@octobus.net>
parents: 46390
diff changeset
71 })
43d63979a75e rust: use HgError in RevlogError and Vfs
Simon Sapin <simon.sapin@octobus.net>
parents: 46390
diff changeset
72 }
43d63979a75e rust: use HgError in RevlogError and Vfs
Simon Sapin <simon.sapin@octobus.net>
parents: 46390
diff changeset
73
43d63979a75e rust: use HgError in RevlogError and Vfs
Simon Sapin <simon.sapin@octobus.net>
parents: 46390
diff changeset
74 let (header, rest) = parse(DocketHeader::from_bytes(input))?;
46390
0800aa42bb4c rust: use the bytes-cast crate to parse persistent nodemaps
Simon Sapin <simon.sapin@octobus.net>
parents: 46167
diff changeset
75 let uid_size = header.uid_size as usize;
46090
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
76 // TODO: do we care about overflow for 4 GB+ nodemap files on 32-bit
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
77 // systems?
46390
0800aa42bb4c rust: use the bytes-cast crate to parse persistent nodemaps
Simon Sapin <simon.sapin@octobus.net>
parents: 46167
diff changeset
78 let tip_node_size = header.tip_node_size.get() as usize;
0800aa42bb4c rust: use the bytes-cast crate to parse persistent nodemaps
Simon Sapin <simon.sapin@octobus.net>
parents: 46167
diff changeset
79 let data_length = header.data_length.get() as usize;
46443
43d63979a75e rust: use HgError in RevlogError and Vfs
Simon Sapin <simon.sapin@octobus.net>
parents: 46390
diff changeset
80 let (uid, rest) = parse(u8::slice_from_bytes(rest, uid_size))?;
43d63979a75e rust: use HgError in RevlogError and Vfs
Simon Sapin <simon.sapin@octobus.net>
parents: 46390
diff changeset
81 let (_tip_node, _rest) =
43d63979a75e rust: use HgError in RevlogError and Vfs
Simon Sapin <simon.sapin@octobus.net>
parents: 46390
diff changeset
82 parse(u8::slice_from_bytes(rest, tip_node_size))?;
43d63979a75e rust: use HgError in RevlogError and Vfs
Simon Sapin <simon.sapin@octobus.net>
parents: 46390
diff changeset
83 let uid = parse(std::str::from_utf8(uid))?;
46090
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
84 let docket = NodeMapDocket { data_length };
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
85
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
86 let data_path = rawdata_path(&docket_path, uid);
46443
43d63979a75e rust: use HgError in RevlogError and Vfs
Simon Sapin <simon.sapin@octobus.net>
parents: 46390
diff changeset
87 // TODO: use `vfs.read()` here when the `persistent-nodemap.mmap`
46090
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
88 // config is false?
46443
43d63979a75e rust: use HgError in RevlogError and Vfs
Simon Sapin <simon.sapin@octobus.net>
parents: 46390
diff changeset
89 if let Some(mmap) = repo
43d63979a75e rust: use HgError in RevlogError and Vfs
Simon Sapin <simon.sapin@octobus.net>
parents: 46390
diff changeset
90 .store_vfs()
43d63979a75e rust: use HgError in RevlogError and Vfs
Simon Sapin <simon.sapin@octobus.net>
parents: 46390
diff changeset
91 .mmap_open(&data_path)
43d63979a75e rust: use HgError in RevlogError and Vfs
Simon Sapin <simon.sapin@octobus.net>
parents: 46390
diff changeset
92 .io_not_found_as_none()?
43d63979a75e rust: use HgError in RevlogError and Vfs
Simon Sapin <simon.sapin@octobus.net>
parents: 46390
diff changeset
93 {
43d63979a75e rust: use HgError in RevlogError and Vfs
Simon Sapin <simon.sapin@octobus.net>
parents: 46390
diff changeset
94 if mmap.len() >= data_length {
43d63979a75e rust: use HgError in RevlogError and Vfs
Simon Sapin <simon.sapin@octobus.net>
parents: 46390
diff changeset
95 Ok(Some((docket, mmap)))
43d63979a75e rust: use HgError in RevlogError and Vfs
Simon Sapin <simon.sapin@octobus.net>
parents: 46390
diff changeset
96 } else {
43d63979a75e rust: use HgError in RevlogError and Vfs
Simon Sapin <simon.sapin@octobus.net>
parents: 46390
diff changeset
97 Err(HgError::corrupted("persistent nodemap too short").into())
46090
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
98 }
46443
43d63979a75e rust: use HgError in RevlogError and Vfs
Simon Sapin <simon.sapin@octobus.net>
parents: 46390
diff changeset
99 } else {
46630
842f2372ced6 rhg: Don?t attempt to read persistent nodemap without .hg/requires opt-in
Simon Sapin <simon.sapin@octobus.net>
parents: 46443
diff changeset
100 // Even if .hg/requires opted in, some revlogs are deemed small
842f2372ced6 rhg: Don?t attempt to read persistent nodemap without .hg/requires opt-in
Simon Sapin <simon.sapin@octobus.net>
parents: 46443
diff changeset
101 // enough to not need a persistent nodemap.
46443
43d63979a75e rust: use HgError in RevlogError and Vfs
Simon Sapin <simon.sapin@octobus.net>
parents: 46390
diff changeset
102 Ok(None)
46090
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
103 }
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
104 }
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
105 }
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
106
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
107 fn rawdata_path(docket_path: &Path, uid: &str) -> PathBuf {
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
108 let docket_name = docket_path
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
109 .file_name()
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
110 .expect("expected a base name")
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
111 .to_str()
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
112 .expect("expected an ASCII file name in the store");
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
113 let prefix = strip_suffix(docket_name, ".n.a")
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
114 .or_else(|| strip_suffix(docket_name, ".n"))
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
115 .expect("expected docket path in .n or .n.a");
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
116 let name = format!("{}-{}.nd", prefix, uid);
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
117 docket_path
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
118 .parent()
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
119 .expect("expected a non-root path")
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
120 .join(name)
9eb07ab3f2d4 rhg: use persistent nodemap when available
Simon Sapin <simon-commits@exyr.org>
parents:
diff changeset
121 }