##// END OF EJS Templates
rust-dirstatemap: remove `removefile` API...
Raphaël Gomès -
r50002:8a17fc50 default
parent child Browse files
Show More
@@ -1,1494 +1,1458 b''
1 use bytes_cast::BytesCast;
1 use bytes_cast::BytesCast;
2 use micro_timer::timed;
2 use micro_timer::timed;
3 use std::borrow::Cow;
3 use std::borrow::Cow;
4 use std::path::PathBuf;
4 use std::path::PathBuf;
5
5
6 use super::on_disk;
6 use super::on_disk;
7 use super::on_disk::DirstateV2ParseError;
7 use super::on_disk::DirstateV2ParseError;
8 use super::owning::OwningDirstateMap;
8 use super::owning::OwningDirstateMap;
9 use super::path_with_basename::WithBasename;
9 use super::path_with_basename::WithBasename;
10 use crate::dirstate::parsers::pack_entry;
10 use crate::dirstate::parsers::pack_entry;
11 use crate::dirstate::parsers::packed_entry_size;
11 use crate::dirstate::parsers::packed_entry_size;
12 use crate::dirstate::parsers::parse_dirstate_entries;
12 use crate::dirstate::parsers::parse_dirstate_entries;
13 use crate::dirstate::CopyMapIter;
13 use crate::dirstate::CopyMapIter;
14 use crate::dirstate::DirstateV2Data;
14 use crate::dirstate::DirstateV2Data;
15 use crate::dirstate::ParentFileData;
15 use crate::dirstate::ParentFileData;
16 use crate::dirstate::StateMapIter;
16 use crate::dirstate::StateMapIter;
17 use crate::dirstate::TruncatedTimestamp;
17 use crate::dirstate::TruncatedTimestamp;
18 use crate::dirstate::SIZE_FROM_OTHER_PARENT;
19 use crate::dirstate::SIZE_NON_NORMAL;
20 use crate::matchers::Matcher;
18 use crate::matchers::Matcher;
21 use crate::utils::hg_path::{HgPath, HgPathBuf};
19 use crate::utils::hg_path::{HgPath, HgPathBuf};
22 use crate::DirstateEntry;
20 use crate::DirstateEntry;
23 use crate::DirstateError;
21 use crate::DirstateError;
24 use crate::DirstateMapError;
22 use crate::DirstateMapError;
25 use crate::DirstateParents;
23 use crate::DirstateParents;
26 use crate::DirstateStatus;
24 use crate::DirstateStatus;
27 use crate::EntryState;
25 use crate::EntryState;
28 use crate::FastHashbrownMap as FastHashMap;
26 use crate::FastHashbrownMap as FastHashMap;
29 use crate::PatternFileWarning;
27 use crate::PatternFileWarning;
30 use crate::StatusError;
28 use crate::StatusError;
31 use crate::StatusOptions;
29 use crate::StatusOptions;
32
30
33 /// Append to an existing data file if the amount of unreachable data (not used
31 /// Append to an existing data file if the amount of unreachable data (not used
34 /// anymore) is less than this fraction of the total amount of existing data.
32 /// anymore) is less than this fraction of the total amount of existing data.
35 const ACCEPTABLE_UNREACHABLE_BYTES_RATIO: f32 = 0.5;
33 const ACCEPTABLE_UNREACHABLE_BYTES_RATIO: f32 = 0.5;
36
34
37 pub struct DirstateMap<'on_disk> {
35 pub struct DirstateMap<'on_disk> {
38 /// Contents of the `.hg/dirstate` file
36 /// Contents of the `.hg/dirstate` file
39 pub(super) on_disk: &'on_disk [u8],
37 pub(super) on_disk: &'on_disk [u8],
40
38
41 pub(super) root: ChildNodes<'on_disk>,
39 pub(super) root: ChildNodes<'on_disk>,
42
40
43 /// Number of nodes anywhere in the tree that have `.entry.is_some()`.
41 /// Number of nodes anywhere in the tree that have `.entry.is_some()`.
44 pub(super) nodes_with_entry_count: u32,
42 pub(super) nodes_with_entry_count: u32,
45
43
46 /// Number of nodes anywhere in the tree that have
44 /// Number of nodes anywhere in the tree that have
47 /// `.copy_source.is_some()`.
45 /// `.copy_source.is_some()`.
48 pub(super) nodes_with_copy_source_count: u32,
46 pub(super) nodes_with_copy_source_count: u32,
49
47
50 /// See on_disk::Header
48 /// See on_disk::Header
51 pub(super) ignore_patterns_hash: on_disk::IgnorePatternsHash,
49 pub(super) ignore_patterns_hash: on_disk::IgnorePatternsHash,
52
50
53 /// How many bytes of `on_disk` are not used anymore
51 /// How many bytes of `on_disk` are not used anymore
54 pub(super) unreachable_bytes: u32,
52 pub(super) unreachable_bytes: u32,
55 }
53 }
56
54
57 /// Using a plain `HgPathBuf` of the full path from the repository root as a
55 /// Using a plain `HgPathBuf` of the full path from the repository root as a
58 /// map key would also work: all paths in a given map have the same parent
56 /// map key would also work: all paths in a given map have the same parent
59 /// path, so comparing full paths gives the same result as comparing base
57 /// path, so comparing full paths gives the same result as comparing base
60 /// names. However `HashMap` would waste time always re-hashing the same
58 /// names. However `HashMap` would waste time always re-hashing the same
61 /// string prefix.
59 /// string prefix.
62 pub(super) type NodeKey<'on_disk> = WithBasename<Cow<'on_disk, HgPath>>;
60 pub(super) type NodeKey<'on_disk> = WithBasename<Cow<'on_disk, HgPath>>;
63
61
64 /// Similar to `&'tree Cow<'on_disk, HgPath>`, but can also be returned
62 /// Similar to `&'tree Cow<'on_disk, HgPath>`, but can also be returned
65 /// for on-disk nodes that don’t actually have a `Cow` to borrow.
63 /// for on-disk nodes that don’t actually have a `Cow` to borrow.
66 pub(super) enum BorrowedPath<'tree, 'on_disk> {
64 pub(super) enum BorrowedPath<'tree, 'on_disk> {
67 InMemory(&'tree HgPathBuf),
65 InMemory(&'tree HgPathBuf),
68 OnDisk(&'on_disk HgPath),
66 OnDisk(&'on_disk HgPath),
69 }
67 }
70
68
71 pub(super) enum ChildNodes<'on_disk> {
69 pub(super) enum ChildNodes<'on_disk> {
72 InMemory(FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
70 InMemory(FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
73 OnDisk(&'on_disk [on_disk::Node]),
71 OnDisk(&'on_disk [on_disk::Node]),
74 }
72 }
75
73
76 pub(super) enum ChildNodesRef<'tree, 'on_disk> {
74 pub(super) enum ChildNodesRef<'tree, 'on_disk> {
77 InMemory(&'tree FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
75 InMemory(&'tree FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
78 OnDisk(&'on_disk [on_disk::Node]),
76 OnDisk(&'on_disk [on_disk::Node]),
79 }
77 }
80
78
81 pub(super) enum NodeRef<'tree, 'on_disk> {
79 pub(super) enum NodeRef<'tree, 'on_disk> {
82 InMemory(&'tree NodeKey<'on_disk>, &'tree Node<'on_disk>),
80 InMemory(&'tree NodeKey<'on_disk>, &'tree Node<'on_disk>),
83 OnDisk(&'on_disk on_disk::Node),
81 OnDisk(&'on_disk on_disk::Node),
84 }
82 }
85
83
86 impl<'tree, 'on_disk> BorrowedPath<'tree, 'on_disk> {
84 impl<'tree, 'on_disk> BorrowedPath<'tree, 'on_disk> {
87 pub fn detach_from_tree(&self) -> Cow<'on_disk, HgPath> {
85 pub fn detach_from_tree(&self) -> Cow<'on_disk, HgPath> {
88 match *self {
86 match *self {
89 BorrowedPath::InMemory(in_memory) => Cow::Owned(in_memory.clone()),
87 BorrowedPath::InMemory(in_memory) => Cow::Owned(in_memory.clone()),
90 BorrowedPath::OnDisk(on_disk) => Cow::Borrowed(on_disk),
88 BorrowedPath::OnDisk(on_disk) => Cow::Borrowed(on_disk),
91 }
89 }
92 }
90 }
93 }
91 }
94
92
95 impl<'tree, 'on_disk> std::ops::Deref for BorrowedPath<'tree, 'on_disk> {
93 impl<'tree, 'on_disk> std::ops::Deref for BorrowedPath<'tree, 'on_disk> {
96 type Target = HgPath;
94 type Target = HgPath;
97
95
98 fn deref(&self) -> &HgPath {
96 fn deref(&self) -> &HgPath {
99 match *self {
97 match *self {
100 BorrowedPath::InMemory(in_memory) => in_memory,
98 BorrowedPath::InMemory(in_memory) => in_memory,
101 BorrowedPath::OnDisk(on_disk) => on_disk,
99 BorrowedPath::OnDisk(on_disk) => on_disk,
102 }
100 }
103 }
101 }
104 }
102 }
105
103
106 impl Default for ChildNodes<'_> {
104 impl Default for ChildNodes<'_> {
107 fn default() -> Self {
105 fn default() -> Self {
108 ChildNodes::InMemory(Default::default())
106 ChildNodes::InMemory(Default::default())
109 }
107 }
110 }
108 }
111
109
112 impl<'on_disk> ChildNodes<'on_disk> {
110 impl<'on_disk> ChildNodes<'on_disk> {
113 pub(super) fn as_ref<'tree>(
111 pub(super) fn as_ref<'tree>(
114 &'tree self,
112 &'tree self,
115 ) -> ChildNodesRef<'tree, 'on_disk> {
113 ) -> ChildNodesRef<'tree, 'on_disk> {
116 match self {
114 match self {
117 ChildNodes::InMemory(nodes) => ChildNodesRef::InMemory(nodes),
115 ChildNodes::InMemory(nodes) => ChildNodesRef::InMemory(nodes),
118 ChildNodes::OnDisk(nodes) => ChildNodesRef::OnDisk(nodes),
116 ChildNodes::OnDisk(nodes) => ChildNodesRef::OnDisk(nodes),
119 }
117 }
120 }
118 }
121
119
122 pub(super) fn is_empty(&self) -> bool {
120 pub(super) fn is_empty(&self) -> bool {
123 match self {
121 match self {
124 ChildNodes::InMemory(nodes) => nodes.is_empty(),
122 ChildNodes::InMemory(nodes) => nodes.is_empty(),
125 ChildNodes::OnDisk(nodes) => nodes.is_empty(),
123 ChildNodes::OnDisk(nodes) => nodes.is_empty(),
126 }
124 }
127 }
125 }
128
126
129 fn make_mut(
127 fn make_mut(
130 &mut self,
128 &mut self,
131 on_disk: &'on_disk [u8],
129 on_disk: &'on_disk [u8],
132 unreachable_bytes: &mut u32,
130 unreachable_bytes: &mut u32,
133 ) -> Result<
131 ) -> Result<
134 &mut FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>,
132 &mut FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>,
135 DirstateV2ParseError,
133 DirstateV2ParseError,
136 > {
134 > {
137 match self {
135 match self {
138 ChildNodes::InMemory(nodes) => Ok(nodes),
136 ChildNodes::InMemory(nodes) => Ok(nodes),
139 ChildNodes::OnDisk(nodes) => {
137 ChildNodes::OnDisk(nodes) => {
140 *unreachable_bytes +=
138 *unreachable_bytes +=
141 std::mem::size_of_val::<[on_disk::Node]>(nodes) as u32;
139 std::mem::size_of_val::<[on_disk::Node]>(nodes) as u32;
142 let nodes = nodes
140 let nodes = nodes
143 .iter()
141 .iter()
144 .map(|node| {
142 .map(|node| {
145 Ok((
143 Ok((
146 node.path(on_disk)?,
144 node.path(on_disk)?,
147 node.to_in_memory_node(on_disk)?,
145 node.to_in_memory_node(on_disk)?,
148 ))
146 ))
149 })
147 })
150 .collect::<Result<_, _>>()?;
148 .collect::<Result<_, _>>()?;
151 *self = ChildNodes::InMemory(nodes);
149 *self = ChildNodes::InMemory(nodes);
152 match self {
150 match self {
153 ChildNodes::InMemory(nodes) => Ok(nodes),
151 ChildNodes::InMemory(nodes) => Ok(nodes),
154 ChildNodes::OnDisk(_) => unreachable!(),
152 ChildNodes::OnDisk(_) => unreachable!(),
155 }
153 }
156 }
154 }
157 }
155 }
158 }
156 }
159 }
157 }
160
158
161 impl<'tree, 'on_disk> ChildNodesRef<'tree, 'on_disk> {
159 impl<'tree, 'on_disk> ChildNodesRef<'tree, 'on_disk> {
162 pub(super) fn get(
160 pub(super) fn get(
163 &self,
161 &self,
164 base_name: &HgPath,
162 base_name: &HgPath,
165 on_disk: &'on_disk [u8],
163 on_disk: &'on_disk [u8],
166 ) -> Result<Option<NodeRef<'tree, 'on_disk>>, DirstateV2ParseError> {
164 ) -> Result<Option<NodeRef<'tree, 'on_disk>>, DirstateV2ParseError> {
167 match self {
165 match self {
168 ChildNodesRef::InMemory(nodes) => Ok(nodes
166 ChildNodesRef::InMemory(nodes) => Ok(nodes
169 .get_key_value(base_name)
167 .get_key_value(base_name)
170 .map(|(k, v)| NodeRef::InMemory(k, v))),
168 .map(|(k, v)| NodeRef::InMemory(k, v))),
171 ChildNodesRef::OnDisk(nodes) => {
169 ChildNodesRef::OnDisk(nodes) => {
172 let mut parse_result = Ok(());
170 let mut parse_result = Ok(());
173 let search_result = nodes.binary_search_by(|node| {
171 let search_result = nodes.binary_search_by(|node| {
174 match node.base_name(on_disk) {
172 match node.base_name(on_disk) {
175 Ok(node_base_name) => node_base_name.cmp(base_name),
173 Ok(node_base_name) => node_base_name.cmp(base_name),
176 Err(e) => {
174 Err(e) => {
177 parse_result = Err(e);
175 parse_result = Err(e);
178 // Dummy comparison result, `search_result` won’t
176 // Dummy comparison result, `search_result` won’t
179 // be used since `parse_result` is an error
177 // be used since `parse_result` is an error
180 std::cmp::Ordering::Equal
178 std::cmp::Ordering::Equal
181 }
179 }
182 }
180 }
183 });
181 });
184 parse_result.map(|()| {
182 parse_result.map(|()| {
185 search_result.ok().map(|i| NodeRef::OnDisk(&nodes[i]))
183 search_result.ok().map(|i| NodeRef::OnDisk(&nodes[i]))
186 })
184 })
187 }
185 }
188 }
186 }
189 }
187 }
190
188
191 /// Iterate in undefined order
189 /// Iterate in undefined order
192 pub(super) fn iter(
190 pub(super) fn iter(
193 &self,
191 &self,
194 ) -> impl Iterator<Item = NodeRef<'tree, 'on_disk>> {
192 ) -> impl Iterator<Item = NodeRef<'tree, 'on_disk>> {
195 match self {
193 match self {
196 ChildNodesRef::InMemory(nodes) => itertools::Either::Left(
194 ChildNodesRef::InMemory(nodes) => itertools::Either::Left(
197 nodes.iter().map(|(k, v)| NodeRef::InMemory(k, v)),
195 nodes.iter().map(|(k, v)| NodeRef::InMemory(k, v)),
198 ),
196 ),
199 ChildNodesRef::OnDisk(nodes) => {
197 ChildNodesRef::OnDisk(nodes) => {
200 itertools::Either::Right(nodes.iter().map(NodeRef::OnDisk))
198 itertools::Either::Right(nodes.iter().map(NodeRef::OnDisk))
201 }
199 }
202 }
200 }
203 }
201 }
204
202
205 /// Iterate in parallel in undefined order
203 /// Iterate in parallel in undefined order
206 pub(super) fn par_iter(
204 pub(super) fn par_iter(
207 &self,
205 &self,
208 ) -> impl rayon::iter::ParallelIterator<Item = NodeRef<'tree, 'on_disk>>
206 ) -> impl rayon::iter::ParallelIterator<Item = NodeRef<'tree, 'on_disk>>
209 {
207 {
210 use rayon::prelude::*;
208 use rayon::prelude::*;
211 match self {
209 match self {
212 ChildNodesRef::InMemory(nodes) => rayon::iter::Either::Left(
210 ChildNodesRef::InMemory(nodes) => rayon::iter::Either::Left(
213 nodes.par_iter().map(|(k, v)| NodeRef::InMemory(k, v)),
211 nodes.par_iter().map(|(k, v)| NodeRef::InMemory(k, v)),
214 ),
212 ),
215 ChildNodesRef::OnDisk(nodes) => rayon::iter::Either::Right(
213 ChildNodesRef::OnDisk(nodes) => rayon::iter::Either::Right(
216 nodes.par_iter().map(NodeRef::OnDisk),
214 nodes.par_iter().map(NodeRef::OnDisk),
217 ),
215 ),
218 }
216 }
219 }
217 }
220
218
221 pub(super) fn sorted(&self) -> Vec<NodeRef<'tree, 'on_disk>> {
219 pub(super) fn sorted(&self) -> Vec<NodeRef<'tree, 'on_disk>> {
222 match self {
220 match self {
223 ChildNodesRef::InMemory(nodes) => {
221 ChildNodesRef::InMemory(nodes) => {
224 let mut vec: Vec<_> = nodes
222 let mut vec: Vec<_> = nodes
225 .iter()
223 .iter()
226 .map(|(k, v)| NodeRef::InMemory(k, v))
224 .map(|(k, v)| NodeRef::InMemory(k, v))
227 .collect();
225 .collect();
228 fn sort_key<'a>(node: &'a NodeRef) -> &'a HgPath {
226 fn sort_key<'a>(node: &'a NodeRef) -> &'a HgPath {
229 match node {
227 match node {
230 NodeRef::InMemory(path, _node) => path.base_name(),
228 NodeRef::InMemory(path, _node) => path.base_name(),
231 NodeRef::OnDisk(_) => unreachable!(),
229 NodeRef::OnDisk(_) => unreachable!(),
232 }
230 }
233 }
231 }
234 // `sort_unstable_by_key` doesn’t allow keys borrowing from the
232 // `sort_unstable_by_key` doesn’t allow keys borrowing from the
235 // value: https://github.com/rust-lang/rust/issues/34162
233 // value: https://github.com/rust-lang/rust/issues/34162
236 vec.sort_unstable_by(|a, b| sort_key(a).cmp(sort_key(b)));
234 vec.sort_unstable_by(|a, b| sort_key(a).cmp(sort_key(b)));
237 vec
235 vec
238 }
236 }
239 ChildNodesRef::OnDisk(nodes) => {
237 ChildNodesRef::OnDisk(nodes) => {
240 // Nodes on disk are already sorted
238 // Nodes on disk are already sorted
241 nodes.iter().map(NodeRef::OnDisk).collect()
239 nodes.iter().map(NodeRef::OnDisk).collect()
242 }
240 }
243 }
241 }
244 }
242 }
245 }
243 }
246
244
247 impl<'tree, 'on_disk> NodeRef<'tree, 'on_disk> {
245 impl<'tree, 'on_disk> NodeRef<'tree, 'on_disk> {
248 pub(super) fn full_path(
246 pub(super) fn full_path(
249 &self,
247 &self,
250 on_disk: &'on_disk [u8],
248 on_disk: &'on_disk [u8],
251 ) -> Result<&'tree HgPath, DirstateV2ParseError> {
249 ) -> Result<&'tree HgPath, DirstateV2ParseError> {
252 match self {
250 match self {
253 NodeRef::InMemory(path, _node) => Ok(path.full_path()),
251 NodeRef::InMemory(path, _node) => Ok(path.full_path()),
254 NodeRef::OnDisk(node) => node.full_path(on_disk),
252 NodeRef::OnDisk(node) => node.full_path(on_disk),
255 }
253 }
256 }
254 }
257
255
258 /// Returns a `BorrowedPath`, which can be turned into a `Cow<'on_disk,
256 /// Returns a `BorrowedPath`, which can be turned into a `Cow<'on_disk,
259 /// HgPath>` detached from `'tree`
257 /// HgPath>` detached from `'tree`
260 pub(super) fn full_path_borrowed(
258 pub(super) fn full_path_borrowed(
261 &self,
259 &self,
262 on_disk: &'on_disk [u8],
260 on_disk: &'on_disk [u8],
263 ) -> Result<BorrowedPath<'tree, 'on_disk>, DirstateV2ParseError> {
261 ) -> Result<BorrowedPath<'tree, 'on_disk>, DirstateV2ParseError> {
264 match self {
262 match self {
265 NodeRef::InMemory(path, _node) => match path.full_path() {
263 NodeRef::InMemory(path, _node) => match path.full_path() {
266 Cow::Borrowed(on_disk) => Ok(BorrowedPath::OnDisk(on_disk)),
264 Cow::Borrowed(on_disk) => Ok(BorrowedPath::OnDisk(on_disk)),
267 Cow::Owned(in_memory) => Ok(BorrowedPath::InMemory(in_memory)),
265 Cow::Owned(in_memory) => Ok(BorrowedPath::InMemory(in_memory)),
268 },
266 },
269 NodeRef::OnDisk(node) => {
267 NodeRef::OnDisk(node) => {
270 Ok(BorrowedPath::OnDisk(node.full_path(on_disk)?))
268 Ok(BorrowedPath::OnDisk(node.full_path(on_disk)?))
271 }
269 }
272 }
270 }
273 }
271 }
274
272
275 pub(super) fn base_name(
273 pub(super) fn base_name(
276 &self,
274 &self,
277 on_disk: &'on_disk [u8],
275 on_disk: &'on_disk [u8],
278 ) -> Result<&'tree HgPath, DirstateV2ParseError> {
276 ) -> Result<&'tree HgPath, DirstateV2ParseError> {
279 match self {
277 match self {
280 NodeRef::InMemory(path, _node) => Ok(path.base_name()),
278 NodeRef::InMemory(path, _node) => Ok(path.base_name()),
281 NodeRef::OnDisk(node) => node.base_name(on_disk),
279 NodeRef::OnDisk(node) => node.base_name(on_disk),
282 }
280 }
283 }
281 }
284
282
285 pub(super) fn children(
283 pub(super) fn children(
286 &self,
284 &self,
287 on_disk: &'on_disk [u8],
285 on_disk: &'on_disk [u8],
288 ) -> Result<ChildNodesRef<'tree, 'on_disk>, DirstateV2ParseError> {
286 ) -> Result<ChildNodesRef<'tree, 'on_disk>, DirstateV2ParseError> {
289 match self {
287 match self {
290 NodeRef::InMemory(_path, node) => Ok(node.children.as_ref()),
288 NodeRef::InMemory(_path, node) => Ok(node.children.as_ref()),
291 NodeRef::OnDisk(node) => {
289 NodeRef::OnDisk(node) => {
292 Ok(ChildNodesRef::OnDisk(node.children(on_disk)?))
290 Ok(ChildNodesRef::OnDisk(node.children(on_disk)?))
293 }
291 }
294 }
292 }
295 }
293 }
296
294
297 pub(super) fn has_copy_source(&self) -> bool {
295 pub(super) fn has_copy_source(&self) -> bool {
298 match self {
296 match self {
299 NodeRef::InMemory(_path, node) => node.copy_source.is_some(),
297 NodeRef::InMemory(_path, node) => node.copy_source.is_some(),
300 NodeRef::OnDisk(node) => node.has_copy_source(),
298 NodeRef::OnDisk(node) => node.has_copy_source(),
301 }
299 }
302 }
300 }
303
301
304 pub(super) fn copy_source(
302 pub(super) fn copy_source(
305 &self,
303 &self,
306 on_disk: &'on_disk [u8],
304 on_disk: &'on_disk [u8],
307 ) -> Result<Option<&'tree HgPath>, DirstateV2ParseError> {
305 ) -> Result<Option<&'tree HgPath>, DirstateV2ParseError> {
308 match self {
306 match self {
309 NodeRef::InMemory(_path, node) => {
307 NodeRef::InMemory(_path, node) => {
310 Ok(node.copy_source.as_ref().map(|s| &**s))
308 Ok(node.copy_source.as_ref().map(|s| &**s))
311 }
309 }
312 NodeRef::OnDisk(node) => node.copy_source(on_disk),
310 NodeRef::OnDisk(node) => node.copy_source(on_disk),
313 }
311 }
314 }
312 }
315 /// Returns a `BorrowedPath`, which can be turned into a `Cow<'on_disk,
313 /// Returns a `BorrowedPath`, which can be turned into a `Cow<'on_disk,
316 /// HgPath>` detached from `'tree`
314 /// HgPath>` detached from `'tree`
317 pub(super) fn copy_source_borrowed(
315 pub(super) fn copy_source_borrowed(
318 &self,
316 &self,
319 on_disk: &'on_disk [u8],
317 on_disk: &'on_disk [u8],
320 ) -> Result<Option<BorrowedPath<'tree, 'on_disk>>, DirstateV2ParseError>
318 ) -> Result<Option<BorrowedPath<'tree, 'on_disk>>, DirstateV2ParseError>
321 {
319 {
322 Ok(match self {
320 Ok(match self {
323 NodeRef::InMemory(_path, node) => {
321 NodeRef::InMemory(_path, node) => {
324 node.copy_source.as_ref().map(|source| match source {
322 node.copy_source.as_ref().map(|source| match source {
325 Cow::Borrowed(on_disk) => BorrowedPath::OnDisk(on_disk),
323 Cow::Borrowed(on_disk) => BorrowedPath::OnDisk(on_disk),
326 Cow::Owned(in_memory) => BorrowedPath::InMemory(in_memory),
324 Cow::Owned(in_memory) => BorrowedPath::InMemory(in_memory),
327 })
325 })
328 }
326 }
329 NodeRef::OnDisk(node) => node
327 NodeRef::OnDisk(node) => node
330 .copy_source(on_disk)?
328 .copy_source(on_disk)?
331 .map(|source| BorrowedPath::OnDisk(source)),
329 .map(|source| BorrowedPath::OnDisk(source)),
332 })
330 })
333 }
331 }
334
332
335 pub(super) fn entry(
333 pub(super) fn entry(
336 &self,
334 &self,
337 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
335 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
338 match self {
336 match self {
339 NodeRef::InMemory(_path, node) => {
337 NodeRef::InMemory(_path, node) => {
340 Ok(node.data.as_entry().copied())
338 Ok(node.data.as_entry().copied())
341 }
339 }
342 NodeRef::OnDisk(node) => node.entry(),
340 NodeRef::OnDisk(node) => node.entry(),
343 }
341 }
344 }
342 }
345
343
346 pub(super) fn state(
344 pub(super) fn state(
347 &self,
345 &self,
348 ) -> Result<Option<EntryState>, DirstateV2ParseError> {
346 ) -> Result<Option<EntryState>, DirstateV2ParseError> {
349 Ok(self.entry()?.and_then(|e| {
347 Ok(self.entry()?.and_then(|e| {
350 if e.any_tracked() {
348 if e.any_tracked() {
351 Some(e.state())
349 Some(e.state())
352 } else {
350 } else {
353 None
351 None
354 }
352 }
355 }))
353 }))
356 }
354 }
357
355
358 pub(super) fn cached_directory_mtime(
356 pub(super) fn cached_directory_mtime(
359 &self,
357 &self,
360 ) -> Result<Option<TruncatedTimestamp>, DirstateV2ParseError> {
358 ) -> Result<Option<TruncatedTimestamp>, DirstateV2ParseError> {
361 match self {
359 match self {
362 NodeRef::InMemory(_path, node) => Ok(match node.data {
360 NodeRef::InMemory(_path, node) => Ok(match node.data {
363 NodeData::CachedDirectory { mtime } => Some(mtime),
361 NodeData::CachedDirectory { mtime } => Some(mtime),
364 _ => None,
362 _ => None,
365 }),
363 }),
366 NodeRef::OnDisk(node) => node.cached_directory_mtime(),
364 NodeRef::OnDisk(node) => node.cached_directory_mtime(),
367 }
365 }
368 }
366 }
369
367
370 pub(super) fn descendants_with_entry_count(&self) -> u32 {
368 pub(super) fn descendants_with_entry_count(&self) -> u32 {
371 match self {
369 match self {
372 NodeRef::InMemory(_path, node) => {
370 NodeRef::InMemory(_path, node) => {
373 node.descendants_with_entry_count
371 node.descendants_with_entry_count
374 }
372 }
375 NodeRef::OnDisk(node) => node.descendants_with_entry_count.get(),
373 NodeRef::OnDisk(node) => node.descendants_with_entry_count.get(),
376 }
374 }
377 }
375 }
378
376
379 pub(super) fn tracked_descendants_count(&self) -> u32 {
377 pub(super) fn tracked_descendants_count(&self) -> u32 {
380 match self {
378 match self {
381 NodeRef::InMemory(_path, node) => node.tracked_descendants_count,
379 NodeRef::InMemory(_path, node) => node.tracked_descendants_count,
382 NodeRef::OnDisk(node) => node.tracked_descendants_count.get(),
380 NodeRef::OnDisk(node) => node.tracked_descendants_count.get(),
383 }
381 }
384 }
382 }
385 }
383 }
386
384
387 /// Represents a file or a directory
385 /// Represents a file or a directory
388 #[derive(Default)]
386 #[derive(Default)]
389 pub(super) struct Node<'on_disk> {
387 pub(super) struct Node<'on_disk> {
390 pub(super) data: NodeData,
388 pub(super) data: NodeData,
391
389
392 pub(super) copy_source: Option<Cow<'on_disk, HgPath>>,
390 pub(super) copy_source: Option<Cow<'on_disk, HgPath>>,
393
391
394 pub(super) children: ChildNodes<'on_disk>,
392 pub(super) children: ChildNodes<'on_disk>,
395
393
396 /// How many (non-inclusive) descendants of this node have an entry.
394 /// How many (non-inclusive) descendants of this node have an entry.
397 pub(super) descendants_with_entry_count: u32,
395 pub(super) descendants_with_entry_count: u32,
398
396
399 /// How many (non-inclusive) descendants of this node have an entry whose
397 /// How many (non-inclusive) descendants of this node have an entry whose
400 /// state is "tracked".
398 /// state is "tracked".
401 pub(super) tracked_descendants_count: u32,
399 pub(super) tracked_descendants_count: u32,
402 }
400 }
403
401
404 pub(super) enum NodeData {
402 pub(super) enum NodeData {
405 Entry(DirstateEntry),
403 Entry(DirstateEntry),
406 CachedDirectory { mtime: TruncatedTimestamp },
404 CachedDirectory { mtime: TruncatedTimestamp },
407 None,
405 None,
408 }
406 }
409
407
410 impl Default for NodeData {
408 impl Default for NodeData {
411 fn default() -> Self {
409 fn default() -> Self {
412 NodeData::None
410 NodeData::None
413 }
411 }
414 }
412 }
415
413
416 impl NodeData {
414 impl NodeData {
417 fn has_entry(&self) -> bool {
415 fn has_entry(&self) -> bool {
418 match self {
416 match self {
419 NodeData::Entry(_) => true,
417 NodeData::Entry(_) => true,
420 _ => false,
418 _ => false,
421 }
419 }
422 }
420 }
423
421
424 fn as_entry(&self) -> Option<&DirstateEntry> {
422 fn as_entry(&self) -> Option<&DirstateEntry> {
425 match self {
423 match self {
426 NodeData::Entry(entry) => Some(entry),
424 NodeData::Entry(entry) => Some(entry),
427 _ => None,
425 _ => None,
428 }
426 }
429 }
427 }
430
428
431 fn as_entry_mut(&mut self) -> Option<&mut DirstateEntry> {
429 fn as_entry_mut(&mut self) -> Option<&mut DirstateEntry> {
432 match self {
430 match self {
433 NodeData::Entry(entry) => Some(entry),
431 NodeData::Entry(entry) => Some(entry),
434 _ => None,
432 _ => None,
435 }
433 }
436 }
434 }
437 }
435 }
438
436
439 impl<'on_disk> DirstateMap<'on_disk> {
437 impl<'on_disk> DirstateMap<'on_disk> {
440 pub(super) fn empty(on_disk: &'on_disk [u8]) -> Self {
438 pub(super) fn empty(on_disk: &'on_disk [u8]) -> Self {
441 Self {
439 Self {
442 on_disk,
440 on_disk,
443 root: ChildNodes::default(),
441 root: ChildNodes::default(),
444 nodes_with_entry_count: 0,
442 nodes_with_entry_count: 0,
445 nodes_with_copy_source_count: 0,
443 nodes_with_copy_source_count: 0,
446 ignore_patterns_hash: [0; on_disk::IGNORE_PATTERNS_HASH_LEN],
444 ignore_patterns_hash: [0; on_disk::IGNORE_PATTERNS_HASH_LEN],
447 unreachable_bytes: 0,
445 unreachable_bytes: 0,
448 }
446 }
449 }
447 }
450
448
451 #[timed]
449 #[timed]
452 pub fn new_v2(
450 pub fn new_v2(
453 on_disk: &'on_disk [u8],
451 on_disk: &'on_disk [u8],
454 data_size: usize,
452 data_size: usize,
455 metadata: &[u8],
453 metadata: &[u8],
456 ) -> Result<Self, DirstateError> {
454 ) -> Result<Self, DirstateError> {
457 if let Some(data) = on_disk.get(..data_size) {
455 if let Some(data) = on_disk.get(..data_size) {
458 Ok(on_disk::read(data, metadata)?)
456 Ok(on_disk::read(data, metadata)?)
459 } else {
457 } else {
460 Err(DirstateV2ParseError.into())
458 Err(DirstateV2ParseError.into())
461 }
459 }
462 }
460 }
463
461
464 #[timed]
462 #[timed]
465 pub fn new_v1(
463 pub fn new_v1(
466 on_disk: &'on_disk [u8],
464 on_disk: &'on_disk [u8],
467 ) -> Result<(Self, Option<DirstateParents>), DirstateError> {
465 ) -> Result<(Self, Option<DirstateParents>), DirstateError> {
468 let mut map = Self::empty(on_disk);
466 let mut map = Self::empty(on_disk);
469 if map.on_disk.is_empty() {
467 if map.on_disk.is_empty() {
470 return Ok((map, None));
468 return Ok((map, None));
471 }
469 }
472
470
473 let parents = parse_dirstate_entries(
471 let parents = parse_dirstate_entries(
474 map.on_disk,
472 map.on_disk,
475 |path, entry, copy_source| {
473 |path, entry, copy_source| {
476 let tracked = entry.state().is_tracked();
474 let tracked = entry.state().is_tracked();
477 let node = Self::get_or_insert_node(
475 let node = Self::get_or_insert_node(
478 map.on_disk,
476 map.on_disk,
479 &mut map.unreachable_bytes,
477 &mut map.unreachable_bytes,
480 &mut map.root,
478 &mut map.root,
481 path,
479 path,
482 WithBasename::to_cow_borrowed,
480 WithBasename::to_cow_borrowed,
483 |ancestor| {
481 |ancestor| {
484 if tracked {
482 if tracked {
485 ancestor.tracked_descendants_count += 1
483 ancestor.tracked_descendants_count += 1
486 }
484 }
487 ancestor.descendants_with_entry_count += 1
485 ancestor.descendants_with_entry_count += 1
488 },
486 },
489 )?;
487 )?;
490 assert!(
488 assert!(
491 !node.data.has_entry(),
489 !node.data.has_entry(),
492 "duplicate dirstate entry in read"
490 "duplicate dirstate entry in read"
493 );
491 );
494 assert!(
492 assert!(
495 node.copy_source.is_none(),
493 node.copy_source.is_none(),
496 "duplicate dirstate entry in read"
494 "duplicate dirstate entry in read"
497 );
495 );
498 node.data = NodeData::Entry(*entry);
496 node.data = NodeData::Entry(*entry);
499 node.copy_source = copy_source.map(Cow::Borrowed);
497 node.copy_source = copy_source.map(Cow::Borrowed);
500 map.nodes_with_entry_count += 1;
498 map.nodes_with_entry_count += 1;
501 if copy_source.is_some() {
499 if copy_source.is_some() {
502 map.nodes_with_copy_source_count += 1
500 map.nodes_with_copy_source_count += 1
503 }
501 }
504 Ok(())
502 Ok(())
505 },
503 },
506 )?;
504 )?;
507 let parents = Some(parents.clone());
505 let parents = Some(parents.clone());
508
506
509 Ok((map, parents))
507 Ok((map, parents))
510 }
508 }
511
509
512 /// Assuming dirstate-v2 format, returns whether the next write should
510 /// Assuming dirstate-v2 format, returns whether the next write should
513 /// append to the existing data file that contains `self.on_disk` (true),
511 /// append to the existing data file that contains `self.on_disk` (true),
514 /// or create a new data file from scratch (false).
512 /// or create a new data file from scratch (false).
515 pub(super) fn write_should_append(&self) -> bool {
513 pub(super) fn write_should_append(&self) -> bool {
516 let ratio = self.unreachable_bytes as f32 / self.on_disk.len() as f32;
514 let ratio = self.unreachable_bytes as f32 / self.on_disk.len() as f32;
517 ratio < ACCEPTABLE_UNREACHABLE_BYTES_RATIO
515 ratio < ACCEPTABLE_UNREACHABLE_BYTES_RATIO
518 }
516 }
519
517
520 fn get_node<'tree>(
518 fn get_node<'tree>(
521 &'tree self,
519 &'tree self,
522 path: &HgPath,
520 path: &HgPath,
523 ) -> Result<Option<NodeRef<'tree, 'on_disk>>, DirstateV2ParseError> {
521 ) -> Result<Option<NodeRef<'tree, 'on_disk>>, DirstateV2ParseError> {
524 let mut children = self.root.as_ref();
522 let mut children = self.root.as_ref();
525 let mut components = path.components();
523 let mut components = path.components();
526 let mut component =
524 let mut component =
527 components.next().expect("expected at least one components");
525 components.next().expect("expected at least one components");
528 loop {
526 loop {
529 if let Some(child) = children.get(component, self.on_disk)? {
527 if let Some(child) = children.get(component, self.on_disk)? {
530 if let Some(next_component) = components.next() {
528 if let Some(next_component) = components.next() {
531 component = next_component;
529 component = next_component;
532 children = child.children(self.on_disk)?;
530 children = child.children(self.on_disk)?;
533 } else {
531 } else {
534 return Ok(Some(child));
532 return Ok(Some(child));
535 }
533 }
536 } else {
534 } else {
537 return Ok(None);
535 return Ok(None);
538 }
536 }
539 }
537 }
540 }
538 }
541
539
542 /// Returns a mutable reference to the node at `path` if it exists
540 /// Returns a mutable reference to the node at `path` if it exists
543 ///
541 ///
544 /// This takes `root` instead of `&mut self` so that callers can mutate
542 /// This takes `root` instead of `&mut self` so that callers can mutate
545 /// other fields while the returned borrow is still valid
543 /// other fields while the returned borrow is still valid
546 fn get_node_mut<'tree>(
544 fn get_node_mut<'tree>(
547 on_disk: &'on_disk [u8],
545 on_disk: &'on_disk [u8],
548 unreachable_bytes: &mut u32,
546 unreachable_bytes: &mut u32,
549 root: &'tree mut ChildNodes<'on_disk>,
547 root: &'tree mut ChildNodes<'on_disk>,
550 path: &HgPath,
548 path: &HgPath,
551 ) -> Result<Option<&'tree mut Node<'on_disk>>, DirstateV2ParseError> {
549 ) -> Result<Option<&'tree mut Node<'on_disk>>, DirstateV2ParseError> {
552 let mut children = root;
550 let mut children = root;
553 let mut components = path.components();
551 let mut components = path.components();
554 let mut component =
552 let mut component =
555 components.next().expect("expected at least one components");
553 components.next().expect("expected at least one components");
556 loop {
554 loop {
557 if let Some(child) = children
555 if let Some(child) = children
558 .make_mut(on_disk, unreachable_bytes)?
556 .make_mut(on_disk, unreachable_bytes)?
559 .get_mut(component)
557 .get_mut(component)
560 {
558 {
561 if let Some(next_component) = components.next() {
559 if let Some(next_component) = components.next() {
562 component = next_component;
560 component = next_component;
563 children = &mut child.children;
561 children = &mut child.children;
564 } else {
562 } else {
565 return Ok(Some(child));
563 return Ok(Some(child));
566 }
564 }
567 } else {
565 } else {
568 return Ok(None);
566 return Ok(None);
569 }
567 }
570 }
568 }
571 }
569 }
572
570
573 pub(super) fn get_or_insert<'tree, 'path>(
571 pub(super) fn get_or_insert<'tree, 'path>(
574 &'tree mut self,
572 &'tree mut self,
575 path: &HgPath,
573 path: &HgPath,
576 ) -> Result<&'tree mut Node<'on_disk>, DirstateV2ParseError> {
574 ) -> Result<&'tree mut Node<'on_disk>, DirstateV2ParseError> {
577 Self::get_or_insert_node(
575 Self::get_or_insert_node(
578 self.on_disk,
576 self.on_disk,
579 &mut self.unreachable_bytes,
577 &mut self.unreachable_bytes,
580 &mut self.root,
578 &mut self.root,
581 path,
579 path,
582 WithBasename::to_cow_owned,
580 WithBasename::to_cow_owned,
583 |_| {},
581 |_| {},
584 )
582 )
585 }
583 }
586
584
587 fn get_or_insert_node<'tree, 'path>(
585 fn get_or_insert_node<'tree, 'path>(
588 on_disk: &'on_disk [u8],
586 on_disk: &'on_disk [u8],
589 unreachable_bytes: &mut u32,
587 unreachable_bytes: &mut u32,
590 root: &'tree mut ChildNodes<'on_disk>,
588 root: &'tree mut ChildNodes<'on_disk>,
591 path: &'path HgPath,
589 path: &'path HgPath,
592 to_cow: impl Fn(
590 to_cow: impl Fn(
593 WithBasename<&'path HgPath>,
591 WithBasename<&'path HgPath>,
594 ) -> WithBasename<Cow<'on_disk, HgPath>>,
592 ) -> WithBasename<Cow<'on_disk, HgPath>>,
595 mut each_ancestor: impl FnMut(&mut Node),
593 mut each_ancestor: impl FnMut(&mut Node),
596 ) -> Result<&'tree mut Node<'on_disk>, DirstateV2ParseError> {
594 ) -> Result<&'tree mut Node<'on_disk>, DirstateV2ParseError> {
597 let mut child_nodes = root;
595 let mut child_nodes = root;
598 let mut inclusive_ancestor_paths =
596 let mut inclusive_ancestor_paths =
599 WithBasename::inclusive_ancestors_of(path);
597 WithBasename::inclusive_ancestors_of(path);
600 let mut ancestor_path = inclusive_ancestor_paths
598 let mut ancestor_path = inclusive_ancestor_paths
601 .next()
599 .next()
602 .expect("expected at least one inclusive ancestor");
600 .expect("expected at least one inclusive ancestor");
603 loop {
601 loop {
604 let (_, child_node) = child_nodes
602 let (_, child_node) = child_nodes
605 .make_mut(on_disk, unreachable_bytes)?
603 .make_mut(on_disk, unreachable_bytes)?
606 .raw_entry_mut()
604 .raw_entry_mut()
607 .from_key(ancestor_path.base_name())
605 .from_key(ancestor_path.base_name())
608 .or_insert_with(|| (to_cow(ancestor_path), Node::default()));
606 .or_insert_with(|| (to_cow(ancestor_path), Node::default()));
609 if let Some(next) = inclusive_ancestor_paths.next() {
607 if let Some(next) = inclusive_ancestor_paths.next() {
610 each_ancestor(child_node);
608 each_ancestor(child_node);
611 ancestor_path = next;
609 ancestor_path = next;
612 child_nodes = &mut child_node.children;
610 child_nodes = &mut child_node.children;
613 } else {
611 } else {
614 return Ok(child_node);
612 return Ok(child_node);
615 }
613 }
616 }
614 }
617 }
615 }
618
616
619 fn reset_state(
617 fn reset_state(
620 &mut self,
618 &mut self,
621 filename: &HgPath,
619 filename: &HgPath,
622 old_entry_opt: Option<DirstateEntry>,
620 old_entry_opt: Option<DirstateEntry>,
623 wc_tracked: bool,
621 wc_tracked: bool,
624 p1_tracked: bool,
622 p1_tracked: bool,
625 p2_info: bool,
623 p2_info: bool,
626 has_meaningful_mtime: bool,
624 has_meaningful_mtime: bool,
627 parent_file_data_opt: Option<ParentFileData>,
625 parent_file_data_opt: Option<ParentFileData>,
628 ) -> Result<(), DirstateError> {
626 ) -> Result<(), DirstateError> {
629 let (had_entry, was_tracked) = match old_entry_opt {
627 let (had_entry, was_tracked) = match old_entry_opt {
630 Some(old_entry) => (true, old_entry.tracked()),
628 Some(old_entry) => (true, old_entry.tracked()),
631 None => (false, false),
629 None => (false, false),
632 };
630 };
633 let node = Self::get_or_insert_node(
631 let node = Self::get_or_insert_node(
634 self.on_disk,
632 self.on_disk,
635 &mut self.unreachable_bytes,
633 &mut self.unreachable_bytes,
636 &mut self.root,
634 &mut self.root,
637 filename,
635 filename,
638 WithBasename::to_cow_owned,
636 WithBasename::to_cow_owned,
639 |ancestor| {
637 |ancestor| {
640 if !had_entry {
638 if !had_entry {
641 ancestor.descendants_with_entry_count += 1;
639 ancestor.descendants_with_entry_count += 1;
642 }
640 }
643 if was_tracked {
641 if was_tracked {
644 if !wc_tracked {
642 if !wc_tracked {
645 ancestor.tracked_descendants_count = ancestor
643 ancestor.tracked_descendants_count = ancestor
646 .tracked_descendants_count
644 .tracked_descendants_count
647 .checked_sub(1)
645 .checked_sub(1)
648 .expect("tracked count to be >= 0");
646 .expect("tracked count to be >= 0");
649 }
647 }
650 } else {
648 } else {
651 if wc_tracked {
649 if wc_tracked {
652 ancestor.tracked_descendants_count += 1;
650 ancestor.tracked_descendants_count += 1;
653 }
651 }
654 }
652 }
655 },
653 },
656 )?;
654 )?;
657
655
658 let v2_data = if let Some(parent_file_data) = parent_file_data_opt {
656 let v2_data = if let Some(parent_file_data) = parent_file_data_opt {
659 DirstateV2Data {
657 DirstateV2Data {
660 wc_tracked,
658 wc_tracked,
661 p1_tracked,
659 p1_tracked,
662 p2_info,
660 p2_info,
663 mode_size: parent_file_data.mode_size,
661 mode_size: parent_file_data.mode_size,
664 mtime: if has_meaningful_mtime {
662 mtime: if has_meaningful_mtime {
665 parent_file_data.mtime
663 parent_file_data.mtime
666 } else {
664 } else {
667 None
665 None
668 },
666 },
669 ..Default::default()
667 ..Default::default()
670 }
668 }
671 } else {
669 } else {
672 DirstateV2Data {
670 DirstateV2Data {
673 wc_tracked,
671 wc_tracked,
674 p1_tracked,
672 p1_tracked,
675 p2_info,
673 p2_info,
676 ..Default::default()
674 ..Default::default()
677 }
675 }
678 };
676 };
679 if !had_entry {
677 if !had_entry {
680 self.nodes_with_entry_count += 1;
678 self.nodes_with_entry_count += 1;
681 }
679 }
682 node.data = NodeData::Entry(DirstateEntry::from_v2_data(v2_data));
680 node.data = NodeData::Entry(DirstateEntry::from_v2_data(v2_data));
683 Ok(())
681 Ok(())
684 }
682 }
685
683
686 fn set_tracked(
684 fn set_tracked(
687 &mut self,
685 &mut self,
688 filename: &HgPath,
686 filename: &HgPath,
689 old_entry_opt: Option<DirstateEntry>,
687 old_entry_opt: Option<DirstateEntry>,
690 ) -> Result<bool, DirstateV2ParseError> {
688 ) -> Result<bool, DirstateV2ParseError> {
691 let was_tracked = old_entry_opt.map_or(false, |e| e.tracked());
689 let was_tracked = old_entry_opt.map_or(false, |e| e.tracked());
692 let had_entry = old_entry_opt.is_some();
690 let had_entry = old_entry_opt.is_some();
693 let tracked_count_increment = if was_tracked { 0 } else { 1 };
691 let tracked_count_increment = if was_tracked { 0 } else { 1 };
694 let mut new = false;
692 let mut new = false;
695
693
696 let node = Self::get_or_insert_node(
694 let node = Self::get_or_insert_node(
697 self.on_disk,
695 self.on_disk,
698 &mut self.unreachable_bytes,
696 &mut self.unreachable_bytes,
699 &mut self.root,
697 &mut self.root,
700 filename,
698 filename,
701 WithBasename::to_cow_owned,
699 WithBasename::to_cow_owned,
702 |ancestor| {
700 |ancestor| {
703 if !had_entry {
701 if !had_entry {
704 ancestor.descendants_with_entry_count += 1;
702 ancestor.descendants_with_entry_count += 1;
705 }
703 }
706
704
707 ancestor.tracked_descendants_count += tracked_count_increment;
705 ancestor.tracked_descendants_count += tracked_count_increment;
708 },
706 },
709 )?;
707 )?;
710 let new_entry = if let Some(old_entry) = old_entry_opt {
708 let new_entry = if let Some(old_entry) = old_entry_opt {
711 let mut e = old_entry.clone();
709 let mut e = old_entry.clone();
712 if e.tracked() {
710 if e.tracked() {
713 // XXX
711 // XXX
714 // This is probably overkill for more case, but we need this to
712 // This is probably overkill for more case, but we need this to
715 // fully replace the `normallookup` call with `set_tracked`
713 // fully replace the `normallookup` call with `set_tracked`
716 // one. Consider smoothing this in the future.
714 // one. Consider smoothing this in the future.
717 e.set_possibly_dirty();
715 e.set_possibly_dirty();
718 } else {
716 } else {
719 new = true;
717 new = true;
720 e.set_tracked();
718 e.set_tracked();
721 }
719 }
722 e
720 e
723 } else {
721 } else {
724 self.nodes_with_entry_count += 1;
722 self.nodes_with_entry_count += 1;
725 new = true;
723 new = true;
726 DirstateEntry::new_tracked()
724 DirstateEntry::new_tracked()
727 };
725 };
728 node.data = NodeData::Entry(new_entry);
726 node.data = NodeData::Entry(new_entry);
729 Ok(new)
727 Ok(new)
730 }
728 }
731
729
732 fn add_or_remove_file(
730 fn add_or_remove_file(
733 &mut self,
731 &mut self,
734 path: &HgPath,
732 path: &HgPath,
735 old_state: Option<EntryState>,
733 old_state: Option<EntryState>,
736 new_entry: DirstateEntry,
734 new_entry: DirstateEntry,
737 ) -> Result<(), DirstateV2ParseError> {
735 ) -> Result<(), DirstateV2ParseError> {
738 let had_entry = old_state.is_some();
736 let had_entry = old_state.is_some();
739 let was_tracked = old_state.map_or(false, |s| s.is_tracked());
737 let was_tracked = old_state.map_or(false, |s| s.is_tracked());
740 let tracked_count_increment =
738 let tracked_count_increment =
741 match (was_tracked, new_entry.state().is_tracked()) {
739 match (was_tracked, new_entry.state().is_tracked()) {
742 (false, true) => 1,
740 (false, true) => 1,
743 (true, false) => -1,
741 (true, false) => -1,
744 _ => 0,
742 _ => 0,
745 };
743 };
746
744
747 let node = Self::get_or_insert_node(
745 let node = Self::get_or_insert_node(
748 self.on_disk,
746 self.on_disk,
749 &mut self.unreachable_bytes,
747 &mut self.unreachable_bytes,
750 &mut self.root,
748 &mut self.root,
751 path,
749 path,
752 WithBasename::to_cow_owned,
750 WithBasename::to_cow_owned,
753 |ancestor| {
751 |ancestor| {
754 if !had_entry {
752 if !had_entry {
755 ancestor.descendants_with_entry_count += 1;
753 ancestor.descendants_with_entry_count += 1;
756 }
754 }
757
755
758 // We can’t use `+= increment` because the counter is unsigned,
756 // We can’t use `+= increment` because the counter is unsigned,
759 // and we want debug builds to detect accidental underflow
757 // and we want debug builds to detect accidental underflow
760 // through zero
758 // through zero
761 match tracked_count_increment {
759 match tracked_count_increment {
762 1 => ancestor.tracked_descendants_count += 1,
760 1 => ancestor.tracked_descendants_count += 1,
763 -1 => ancestor.tracked_descendants_count -= 1,
761 -1 => ancestor.tracked_descendants_count -= 1,
764 _ => {}
762 _ => {}
765 }
763 }
766 },
764 },
767 )?;
765 )?;
768 if !had_entry {
766 if !had_entry {
769 self.nodes_with_entry_count += 1
767 self.nodes_with_entry_count += 1
770 }
768 }
771 node.data = NodeData::Entry(new_entry);
769 node.data = NodeData::Entry(new_entry);
772 Ok(())
770 Ok(())
773 }
771 }
774
772
775 /// It is the responsibility of the caller to know that there was an entry
773 /// It is the responsibility of the caller to know that there was an entry
776 /// there before. Does not handle the removal of copy source
774 /// there before. Does not handle the removal of copy source
777 fn set_untracked(
775 fn set_untracked(
778 &mut self,
776 &mut self,
779 filename: &HgPath,
777 filename: &HgPath,
780 old_entry: DirstateEntry,
778 old_entry: DirstateEntry,
781 ) -> Result<(), DirstateV2ParseError> {
779 ) -> Result<(), DirstateV2ParseError> {
782 let node = Self::get_or_insert_node(
780 let node = Self::get_or_insert_node(
783 self.on_disk,
781 self.on_disk,
784 &mut self.unreachable_bytes,
782 &mut self.unreachable_bytes,
785 &mut self.root,
783 &mut self.root,
786 filename,
784 filename,
787 WithBasename::to_cow_owned,
785 WithBasename::to_cow_owned,
788 |ancestor| {
786 |ancestor| {
789 ancestor.tracked_descendants_count = ancestor
787 ancestor.tracked_descendants_count = ancestor
790 .tracked_descendants_count
788 .tracked_descendants_count
791 .checked_sub(1)
789 .checked_sub(1)
792 .expect("tracked_descendants_count should be >= 0");
790 .expect("tracked_descendants_count should be >= 0");
793 },
791 },
794 )?;
792 )?;
795 let mut new_entry = old_entry.clone();
793 let mut new_entry = old_entry.clone();
796 new_entry.set_untracked();
794 new_entry.set_untracked();
797 node.data = NodeData::Entry(new_entry);
795 node.data = NodeData::Entry(new_entry);
798 Ok(())
796 Ok(())
799 }
797 }
800
798
801 fn set_clean(
799 fn set_clean(
802 &mut self,
800 &mut self,
803 filename: &HgPath,
801 filename: &HgPath,
804 old_entry: DirstateEntry,
802 old_entry: DirstateEntry,
805 mode: u32,
803 mode: u32,
806 size: u32,
804 size: u32,
807 mtime: TruncatedTimestamp,
805 mtime: TruncatedTimestamp,
808 ) -> Result<(), DirstateError> {
806 ) -> Result<(), DirstateError> {
809 let node = Self::get_or_insert_node(
807 let node = Self::get_or_insert_node(
810 self.on_disk,
808 self.on_disk,
811 &mut self.unreachable_bytes,
809 &mut self.unreachable_bytes,
812 &mut self.root,
810 &mut self.root,
813 filename,
811 filename,
814 WithBasename::to_cow_owned,
812 WithBasename::to_cow_owned,
815 |ancestor| {
813 |ancestor| {
816 if !old_entry.tracked() {
814 if !old_entry.tracked() {
817 ancestor.tracked_descendants_count += 1;
815 ancestor.tracked_descendants_count += 1;
818 }
816 }
819 },
817 },
820 )?;
818 )?;
821 let mut new_entry = old_entry.clone();
819 let mut new_entry = old_entry.clone();
822 new_entry.set_clean(mode, size, mtime);
820 new_entry.set_clean(mode, size, mtime);
823 node.data = NodeData::Entry(new_entry);
821 node.data = NodeData::Entry(new_entry);
824 Ok(())
822 Ok(())
825 }
823 }
826
824
827 fn set_possibly_dirty(
825 fn set_possibly_dirty(
828 &mut self,
826 &mut self,
829 filename: &HgPath,
827 filename: &HgPath,
830 ) -> Result<(), DirstateError> {
828 ) -> Result<(), DirstateError> {
831 let node = Self::get_or_insert_node(
829 let node = Self::get_or_insert_node(
832 self.on_disk,
830 self.on_disk,
833 &mut self.unreachable_bytes,
831 &mut self.unreachable_bytes,
834 &mut self.root,
832 &mut self.root,
835 filename,
833 filename,
836 WithBasename::to_cow_owned,
834 WithBasename::to_cow_owned,
837 |_ancestor| {},
835 |_ancestor| {},
838 )?;
836 )?;
839 let entry = node.data.as_entry_mut().expect("entry should exist");
837 let entry = node.data.as_entry_mut().expect("entry should exist");
840 entry.set_possibly_dirty();
838 entry.set_possibly_dirty();
841 node.data = NodeData::Entry(*entry);
839 node.data = NodeData::Entry(*entry);
842 Ok(())
840 Ok(())
843 }
841 }
844
842
845 fn iter_nodes<'tree>(
843 fn iter_nodes<'tree>(
846 &'tree self,
844 &'tree self,
847 ) -> impl Iterator<
845 ) -> impl Iterator<
848 Item = Result<NodeRef<'tree, 'on_disk>, DirstateV2ParseError>,
846 Item = Result<NodeRef<'tree, 'on_disk>, DirstateV2ParseError>,
849 > + 'tree {
847 > + 'tree {
850 // Depth first tree traversal.
848 // Depth first tree traversal.
851 //
849 //
852 // If we could afford internal iteration and recursion,
850 // If we could afford internal iteration and recursion,
853 // this would look like:
851 // this would look like:
854 //
852 //
855 // ```
853 // ```
856 // fn traverse_children(
854 // fn traverse_children(
857 // children: &ChildNodes,
855 // children: &ChildNodes,
858 // each: &mut impl FnMut(&Node),
856 // each: &mut impl FnMut(&Node),
859 // ) {
857 // ) {
860 // for child in children.values() {
858 // for child in children.values() {
861 // traverse_children(&child.children, each);
859 // traverse_children(&child.children, each);
862 // each(child);
860 // each(child);
863 // }
861 // }
864 // }
862 // }
865 // ```
863 // ```
866 //
864 //
867 // However we want an external iterator and therefore can’t use the
865 // However we want an external iterator and therefore can’t use the
868 // call stack. Use an explicit stack instead:
866 // call stack. Use an explicit stack instead:
869 let mut stack = Vec::new();
867 let mut stack = Vec::new();
870 let mut iter = self.root.as_ref().iter();
868 let mut iter = self.root.as_ref().iter();
871 std::iter::from_fn(move || {
869 std::iter::from_fn(move || {
872 while let Some(child_node) = iter.next() {
870 while let Some(child_node) = iter.next() {
873 let children = match child_node.children(self.on_disk) {
871 let children = match child_node.children(self.on_disk) {
874 Ok(children) => children,
872 Ok(children) => children,
875 Err(error) => return Some(Err(error)),
873 Err(error) => return Some(Err(error)),
876 };
874 };
877 // Pseudo-recursion
875 // Pseudo-recursion
878 let new_iter = children.iter();
876 let new_iter = children.iter();
879 let old_iter = std::mem::replace(&mut iter, new_iter);
877 let old_iter = std::mem::replace(&mut iter, new_iter);
880 stack.push((child_node, old_iter));
878 stack.push((child_node, old_iter));
881 }
879 }
882 // Found the end of a `children.iter()` iterator.
880 // Found the end of a `children.iter()` iterator.
883 if let Some((child_node, next_iter)) = stack.pop() {
881 if let Some((child_node, next_iter)) = stack.pop() {
884 // "Return" from pseudo-recursion by restoring state from the
882 // "Return" from pseudo-recursion by restoring state from the
885 // explicit stack
883 // explicit stack
886 iter = next_iter;
884 iter = next_iter;
887
885
888 Some(Ok(child_node))
886 Some(Ok(child_node))
889 } else {
887 } else {
890 // Reached the bottom of the stack, we’re done
888 // Reached the bottom of the stack, we’re done
891 None
889 None
892 }
890 }
893 })
891 })
894 }
892 }
895
893
896 fn count_dropped_path(unreachable_bytes: &mut u32, path: &Cow<HgPath>) {
894 fn count_dropped_path(unreachable_bytes: &mut u32, path: &Cow<HgPath>) {
897 if let Cow::Borrowed(path) = path {
895 if let Cow::Borrowed(path) = path {
898 *unreachable_bytes += path.len() as u32
896 *unreachable_bytes += path.len() as u32
899 }
897 }
900 }
898 }
901 }
899 }
902
900
903 /// Like `Iterator::filter_map`, but over a fallible iterator of `Result`s.
901 /// Like `Iterator::filter_map`, but over a fallible iterator of `Result`s.
904 ///
902 ///
905 /// The callback is only called for incoming `Ok` values. Errors are passed
903 /// The callback is only called for incoming `Ok` values. Errors are passed
906 /// through as-is. In order to let it use the `?` operator the callback is
904 /// through as-is. In order to let it use the `?` operator the callback is
907 /// expected to return a `Result` of `Option`, instead of an `Option` of
905 /// expected to return a `Result` of `Option`, instead of an `Option` of
908 /// `Result`.
906 /// `Result`.
909 fn filter_map_results<'a, I, F, A, B, E>(
907 fn filter_map_results<'a, I, F, A, B, E>(
910 iter: I,
908 iter: I,
911 f: F,
909 f: F,
912 ) -> impl Iterator<Item = Result<B, E>> + 'a
910 ) -> impl Iterator<Item = Result<B, E>> + 'a
913 where
911 where
914 I: Iterator<Item = Result<A, E>> + 'a,
912 I: Iterator<Item = Result<A, E>> + 'a,
915 F: Fn(A) -> Result<Option<B>, E> + 'a,
913 F: Fn(A) -> Result<Option<B>, E> + 'a,
916 {
914 {
917 iter.filter_map(move |result| match result {
915 iter.filter_map(move |result| match result {
918 Ok(node) => f(node).transpose(),
916 Ok(node) => f(node).transpose(),
919 Err(e) => Some(Err(e)),
917 Err(e) => Some(Err(e)),
920 })
918 })
921 }
919 }
922
920
923 impl OwningDirstateMap {
921 impl OwningDirstateMap {
924 pub fn clear(&mut self) {
922 pub fn clear(&mut self) {
925 self.with_dmap_mut(|map| {
923 self.with_dmap_mut(|map| {
926 map.root = Default::default();
924 map.root = Default::default();
927 map.nodes_with_entry_count = 0;
925 map.nodes_with_entry_count = 0;
928 map.nodes_with_copy_source_count = 0;
926 map.nodes_with_copy_source_count = 0;
929 });
927 });
930 }
928 }
931
929
932 pub fn set_entry(
930 pub fn set_entry(
933 &mut self,
931 &mut self,
934 filename: &HgPath,
932 filename: &HgPath,
935 entry: DirstateEntry,
933 entry: DirstateEntry,
936 ) -> Result<(), DirstateV2ParseError> {
934 ) -> Result<(), DirstateV2ParseError> {
937 self.with_dmap_mut(|map| {
935 self.with_dmap_mut(|map| {
938 map.get_or_insert(&filename)?.data = NodeData::Entry(entry);
936 map.get_or_insert(&filename)?.data = NodeData::Entry(entry);
939 Ok(())
937 Ok(())
940 })
938 })
941 }
939 }
942
940
943 pub fn add_file(
941 pub fn add_file(
944 &mut self,
942 &mut self,
945 filename: &HgPath,
943 filename: &HgPath,
946 entry: DirstateEntry,
944 entry: DirstateEntry,
947 ) -> Result<(), DirstateError> {
945 ) -> Result<(), DirstateError> {
948 let old_state = self.get(filename)?.map(|e| e.state());
946 let old_state = self.get(filename)?.map(|e| e.state());
949 self.with_dmap_mut(|map| {
947 self.with_dmap_mut(|map| {
950 Ok(map.add_or_remove_file(filename, old_state, entry)?)
948 Ok(map.add_or_remove_file(filename, old_state, entry)?)
951 })
949 })
952 }
950 }
953
951
954 pub fn set_tracked(
952 pub fn set_tracked(
955 &mut self,
953 &mut self,
956 filename: &HgPath,
954 filename: &HgPath,
957 ) -> Result<bool, DirstateV2ParseError> {
955 ) -> Result<bool, DirstateV2ParseError> {
958 let old_entry_opt = self.get(filename)?;
956 let old_entry_opt = self.get(filename)?;
959 self.with_dmap_mut(|map| map.set_tracked(filename, old_entry_opt))
957 self.with_dmap_mut(|map| map.set_tracked(filename, old_entry_opt))
960 }
958 }
961
959
962 pub fn set_untracked(
960 pub fn set_untracked(
963 &mut self,
961 &mut self,
964 filename: &HgPath,
962 filename: &HgPath,
965 ) -> Result<bool, DirstateError> {
963 ) -> Result<bool, DirstateError> {
966 let old_entry_opt = self.get(filename)?;
964 let old_entry_opt = self.get(filename)?;
967 match old_entry_opt {
965 match old_entry_opt {
968 None => Ok(false),
966 None => Ok(false),
969 Some(old_entry) => {
967 Some(old_entry) => {
970 if !old_entry.tracked() {
968 if !old_entry.tracked() {
971 // `DirstateMap::set_untracked` is not a noop if
969 // `DirstateMap::set_untracked` is not a noop if
972 // already not tracked as it will decrement the
970 // already not tracked as it will decrement the
973 // tracked counters while going down.
971 // tracked counters while going down.
974 return Ok(true);
972 return Ok(true);
975 }
973 }
976 if old_entry.added() {
974 if old_entry.added() {
977 // Untracking an "added" entry will just result in a
975 // Untracking an "added" entry will just result in a
978 // worthless entry (and other parts of the code will
976 // worthless entry (and other parts of the code will
979 // complain about it), just drop it entirely.
977 // complain about it), just drop it entirely.
980 self.drop_entry_and_copy_source(filename)?;
978 self.drop_entry_and_copy_source(filename)?;
981 return Ok(true);
979 return Ok(true);
982 }
980 }
983 if !old_entry.p2_info() {
981 if !old_entry.p2_info() {
984 self.copy_map_remove(filename)?;
982 self.copy_map_remove(filename)?;
985 }
983 }
986
984
987 self.with_dmap_mut(|map| {
985 self.with_dmap_mut(|map| {
988 map.set_untracked(filename, old_entry)?;
986 map.set_untracked(filename, old_entry)?;
989 Ok(true)
987 Ok(true)
990 })
988 })
991 }
989 }
992 }
990 }
993 }
991 }
994
992
995 pub fn set_clean(
993 pub fn set_clean(
996 &mut self,
994 &mut self,
997 filename: &HgPath,
995 filename: &HgPath,
998 mode: u32,
996 mode: u32,
999 size: u32,
997 size: u32,
1000 mtime: TruncatedTimestamp,
998 mtime: TruncatedTimestamp,
1001 ) -> Result<(), DirstateError> {
999 ) -> Result<(), DirstateError> {
1002 let old_entry = match self.get(filename)? {
1000 let old_entry = match self.get(filename)? {
1003 None => {
1001 None => {
1004 return Err(
1002 return Err(
1005 DirstateMapError::PathNotFound(filename.into()).into()
1003 DirstateMapError::PathNotFound(filename.into()).into()
1006 )
1004 )
1007 }
1005 }
1008 Some(e) => e,
1006 Some(e) => e,
1009 };
1007 };
1010 self.copy_map_remove(filename)?;
1008 self.copy_map_remove(filename)?;
1011 self.with_dmap_mut(|map| {
1009 self.with_dmap_mut(|map| {
1012 map.set_clean(filename, old_entry, mode, size, mtime)
1010 map.set_clean(filename, old_entry, mode, size, mtime)
1013 })
1011 })
1014 }
1012 }
1015
1013
1016 pub fn set_possibly_dirty(
1014 pub fn set_possibly_dirty(
1017 &mut self,
1015 &mut self,
1018 filename: &HgPath,
1016 filename: &HgPath,
1019 ) -> Result<(), DirstateError> {
1017 ) -> Result<(), DirstateError> {
1020 if self.get(filename)?.is_none() {
1018 if self.get(filename)?.is_none() {
1021 return Err(DirstateMapError::PathNotFound(filename.into()).into());
1019 return Err(DirstateMapError::PathNotFound(filename.into()).into());
1022 }
1020 }
1023 self.with_dmap_mut(|map| map.set_possibly_dirty(filename))
1021 self.with_dmap_mut(|map| map.set_possibly_dirty(filename))
1024 }
1022 }
1025
1023
1026 pub fn reset_state(
1024 pub fn reset_state(
1027 &mut self,
1025 &mut self,
1028 filename: &HgPath,
1026 filename: &HgPath,
1029 wc_tracked: bool,
1027 wc_tracked: bool,
1030 p1_tracked: bool,
1028 p1_tracked: bool,
1031 p2_info: bool,
1029 p2_info: bool,
1032 has_meaningful_mtime: bool,
1030 has_meaningful_mtime: bool,
1033 parent_file_data_opt: Option<ParentFileData>,
1031 parent_file_data_opt: Option<ParentFileData>,
1034 ) -> Result<(), DirstateError> {
1032 ) -> Result<(), DirstateError> {
1035 if !(p1_tracked || p2_info || wc_tracked) {
1033 if !(p1_tracked || p2_info || wc_tracked) {
1036 self.drop_entry_and_copy_source(filename)?;
1034 self.drop_entry_and_copy_source(filename)?;
1037 return Ok(());
1035 return Ok(());
1038 }
1036 }
1039 self.copy_map_remove(filename)?;
1037 self.copy_map_remove(filename)?;
1040 let old_entry_opt = self.get(filename)?;
1038 let old_entry_opt = self.get(filename)?;
1041 self.with_dmap_mut(|map| {
1039 self.with_dmap_mut(|map| {
1042 map.reset_state(
1040 map.reset_state(
1043 filename,
1041 filename,
1044 old_entry_opt,
1042 old_entry_opt,
1045 wc_tracked,
1043 wc_tracked,
1046 p1_tracked,
1044 p1_tracked,
1047 p2_info,
1045 p2_info,
1048 has_meaningful_mtime,
1046 has_meaningful_mtime,
1049 parent_file_data_opt,
1047 parent_file_data_opt,
1050 )
1048 )
1051 })
1049 })
1052 }
1050 }
1053
1051
1054 pub fn remove_file(
1055 &mut self,
1056 filename: &HgPath,
1057 in_merge: bool,
1058 ) -> Result<(), DirstateError> {
1059 let old_entry_opt = self.get(filename)?;
1060 let old_state = old_entry_opt.map(|e| e.state());
1061 let mut size = 0;
1062 if in_merge {
1063 // XXX we should not be able to have 'm' state and 'FROM_P2' if not
1064 // during a merge. So I (marmoute) am not sure we need the
1065 // conditionnal at all. Adding double checking this with assert
1066 // would be nice.
1067 if let Some(old_entry) = old_entry_opt {
1068 // backup the previous state
1069 if old_entry.state() == EntryState::Merged {
1070 size = SIZE_NON_NORMAL;
1071 } else if old_entry.state() == EntryState::Normal
1072 && old_entry.size() == SIZE_FROM_OTHER_PARENT
1073 {
1074 // other parent
1075 size = SIZE_FROM_OTHER_PARENT;
1076 }
1077 }
1078 }
1079 if size == 0 {
1080 self.copy_map_remove(filename)?;
1081 }
1082 self.with_dmap_mut(|map| {
1083 let entry = DirstateEntry::new_removed(size);
1084 Ok(map.add_or_remove_file(filename, old_state, entry)?)
1085 })
1086 }
1087
1088 pub fn drop_entry_and_copy_source(
1052 pub fn drop_entry_and_copy_source(
1089 &mut self,
1053 &mut self,
1090 filename: &HgPath,
1054 filename: &HgPath,
1091 ) -> Result<(), DirstateError> {
1055 ) -> Result<(), DirstateError> {
1092 let was_tracked = self
1056 let was_tracked = self
1093 .get(filename)?
1057 .get(filename)?
1094 .map_or(false, |e| e.state().is_tracked());
1058 .map_or(false, |e| e.state().is_tracked());
1095 struct Dropped {
1059 struct Dropped {
1096 was_tracked: bool,
1060 was_tracked: bool,
1097 had_entry: bool,
1061 had_entry: bool,
1098 had_copy_source: bool,
1062 had_copy_source: bool,
1099 }
1063 }
1100
1064
1101 /// If this returns `Ok(Some((dropped, removed)))`, then
1065 /// If this returns `Ok(Some((dropped, removed)))`, then
1102 ///
1066 ///
1103 /// * `dropped` is about the leaf node that was at `filename`
1067 /// * `dropped` is about the leaf node that was at `filename`
1104 /// * `removed` is whether this particular level of recursion just
1068 /// * `removed` is whether this particular level of recursion just
1105 /// removed a node in `nodes`.
1069 /// removed a node in `nodes`.
1106 fn recur<'on_disk>(
1070 fn recur<'on_disk>(
1107 on_disk: &'on_disk [u8],
1071 on_disk: &'on_disk [u8],
1108 unreachable_bytes: &mut u32,
1072 unreachable_bytes: &mut u32,
1109 nodes: &mut ChildNodes<'on_disk>,
1073 nodes: &mut ChildNodes<'on_disk>,
1110 path: &HgPath,
1074 path: &HgPath,
1111 ) -> Result<Option<(Dropped, bool)>, DirstateV2ParseError> {
1075 ) -> Result<Option<(Dropped, bool)>, DirstateV2ParseError> {
1112 let (first_path_component, rest_of_path) =
1076 let (first_path_component, rest_of_path) =
1113 path.split_first_component();
1077 path.split_first_component();
1114 let nodes = nodes.make_mut(on_disk, unreachable_bytes)?;
1078 let nodes = nodes.make_mut(on_disk, unreachable_bytes)?;
1115 let node = if let Some(node) = nodes.get_mut(first_path_component)
1079 let node = if let Some(node) = nodes.get_mut(first_path_component)
1116 {
1080 {
1117 node
1081 node
1118 } else {
1082 } else {
1119 return Ok(None);
1083 return Ok(None);
1120 };
1084 };
1121 let dropped;
1085 let dropped;
1122 if let Some(rest) = rest_of_path {
1086 if let Some(rest) = rest_of_path {
1123 if let Some((d, removed)) = recur(
1087 if let Some((d, removed)) = recur(
1124 on_disk,
1088 on_disk,
1125 unreachable_bytes,
1089 unreachable_bytes,
1126 &mut node.children,
1090 &mut node.children,
1127 rest,
1091 rest,
1128 )? {
1092 )? {
1129 dropped = d;
1093 dropped = d;
1130 if dropped.had_entry {
1094 if dropped.had_entry {
1131 node.descendants_with_entry_count = node
1095 node.descendants_with_entry_count = node
1132 .descendants_with_entry_count
1096 .descendants_with_entry_count
1133 .checked_sub(1)
1097 .checked_sub(1)
1134 .expect(
1098 .expect(
1135 "descendants_with_entry_count should be >= 0",
1099 "descendants_with_entry_count should be >= 0",
1136 );
1100 );
1137 }
1101 }
1138 if dropped.was_tracked {
1102 if dropped.was_tracked {
1139 node.tracked_descendants_count = node
1103 node.tracked_descendants_count = node
1140 .tracked_descendants_count
1104 .tracked_descendants_count
1141 .checked_sub(1)
1105 .checked_sub(1)
1142 .expect(
1106 .expect(
1143 "tracked_descendants_count should be >= 0",
1107 "tracked_descendants_count should be >= 0",
1144 );
1108 );
1145 }
1109 }
1146
1110
1147 // Directory caches must be invalidated when removing a
1111 // Directory caches must be invalidated when removing a
1148 // child node
1112 // child node
1149 if removed {
1113 if removed {
1150 if let NodeData::CachedDirectory { .. } = &node.data {
1114 if let NodeData::CachedDirectory { .. } = &node.data {
1151 node.data = NodeData::None
1115 node.data = NodeData::None
1152 }
1116 }
1153 }
1117 }
1154 } else {
1118 } else {
1155 return Ok(None);
1119 return Ok(None);
1156 }
1120 }
1157 } else {
1121 } else {
1158 let entry = node.data.as_entry();
1122 let entry = node.data.as_entry();
1159 let was_tracked = entry.map_or(false, |entry| entry.tracked());
1123 let was_tracked = entry.map_or(false, |entry| entry.tracked());
1160 let had_entry = entry.is_some();
1124 let had_entry = entry.is_some();
1161 if had_entry {
1125 if had_entry {
1162 node.data = NodeData::None
1126 node.data = NodeData::None
1163 }
1127 }
1164 let mut had_copy_source = false;
1128 let mut had_copy_source = false;
1165 if let Some(source) = &node.copy_source {
1129 if let Some(source) = &node.copy_source {
1166 DirstateMap::count_dropped_path(unreachable_bytes, source);
1130 DirstateMap::count_dropped_path(unreachable_bytes, source);
1167 had_copy_source = true;
1131 had_copy_source = true;
1168 node.copy_source = None
1132 node.copy_source = None
1169 }
1133 }
1170 dropped = Dropped {
1134 dropped = Dropped {
1171 was_tracked,
1135 was_tracked,
1172 had_entry,
1136 had_entry,
1173 had_copy_source,
1137 had_copy_source,
1174 };
1138 };
1175 }
1139 }
1176 // After recursion, for both leaf (rest_of_path is None) nodes and
1140 // After recursion, for both leaf (rest_of_path is None) nodes and
1177 // parent nodes, remove a node if it just became empty.
1141 // parent nodes, remove a node if it just became empty.
1178 let remove = !node.data.has_entry()
1142 let remove = !node.data.has_entry()
1179 && node.copy_source.is_none()
1143 && node.copy_source.is_none()
1180 && node.children.is_empty();
1144 && node.children.is_empty();
1181 if remove {
1145 if remove {
1182 let (key, _) =
1146 let (key, _) =
1183 nodes.remove_entry(first_path_component).unwrap();
1147 nodes.remove_entry(first_path_component).unwrap();
1184 DirstateMap::count_dropped_path(
1148 DirstateMap::count_dropped_path(
1185 unreachable_bytes,
1149 unreachable_bytes,
1186 key.full_path(),
1150 key.full_path(),
1187 )
1151 )
1188 }
1152 }
1189 Ok(Some((dropped, remove)))
1153 Ok(Some((dropped, remove)))
1190 }
1154 }
1191
1155
1192 self.with_dmap_mut(|map| {
1156 self.with_dmap_mut(|map| {
1193 if let Some((dropped, _removed)) = recur(
1157 if let Some((dropped, _removed)) = recur(
1194 map.on_disk,
1158 map.on_disk,
1195 &mut map.unreachable_bytes,
1159 &mut map.unreachable_bytes,
1196 &mut map.root,
1160 &mut map.root,
1197 filename,
1161 filename,
1198 )? {
1162 )? {
1199 if dropped.had_entry {
1163 if dropped.had_entry {
1200 map.nodes_with_entry_count = map
1164 map.nodes_with_entry_count = map
1201 .nodes_with_entry_count
1165 .nodes_with_entry_count
1202 .checked_sub(1)
1166 .checked_sub(1)
1203 .expect("nodes_with_entry_count should be >= 0");
1167 .expect("nodes_with_entry_count should be >= 0");
1204 }
1168 }
1205 if dropped.had_copy_source {
1169 if dropped.had_copy_source {
1206 map.nodes_with_copy_source_count = map
1170 map.nodes_with_copy_source_count = map
1207 .nodes_with_copy_source_count
1171 .nodes_with_copy_source_count
1208 .checked_sub(1)
1172 .checked_sub(1)
1209 .expect("nodes_with_copy_source_count should be >= 0");
1173 .expect("nodes_with_copy_source_count should be >= 0");
1210 }
1174 }
1211 } else {
1175 } else {
1212 debug_assert!(!was_tracked);
1176 debug_assert!(!was_tracked);
1213 }
1177 }
1214 Ok(())
1178 Ok(())
1215 })
1179 })
1216 }
1180 }
1217
1181
1218 pub fn has_tracked_dir(
1182 pub fn has_tracked_dir(
1219 &mut self,
1183 &mut self,
1220 directory: &HgPath,
1184 directory: &HgPath,
1221 ) -> Result<bool, DirstateError> {
1185 ) -> Result<bool, DirstateError> {
1222 self.with_dmap_mut(|map| {
1186 self.with_dmap_mut(|map| {
1223 if let Some(node) = map.get_node(directory)? {
1187 if let Some(node) = map.get_node(directory)? {
1224 // A node without a `DirstateEntry` was created to hold child
1188 // A node without a `DirstateEntry` was created to hold child
1225 // nodes, and is therefore a directory.
1189 // nodes, and is therefore a directory.
1226 let state = node.state()?;
1190 let state = node.state()?;
1227 Ok(state.is_none() && node.tracked_descendants_count() > 0)
1191 Ok(state.is_none() && node.tracked_descendants_count() > 0)
1228 } else {
1192 } else {
1229 Ok(false)
1193 Ok(false)
1230 }
1194 }
1231 })
1195 })
1232 }
1196 }
1233
1197
1234 pub fn has_dir(
1198 pub fn has_dir(
1235 &mut self,
1199 &mut self,
1236 directory: &HgPath,
1200 directory: &HgPath,
1237 ) -> Result<bool, DirstateError> {
1201 ) -> Result<bool, DirstateError> {
1238 self.with_dmap_mut(|map| {
1202 self.with_dmap_mut(|map| {
1239 if let Some(node) = map.get_node(directory)? {
1203 if let Some(node) = map.get_node(directory)? {
1240 // A node without a `DirstateEntry` was created to hold child
1204 // A node without a `DirstateEntry` was created to hold child
1241 // nodes, and is therefore a directory.
1205 // nodes, and is therefore a directory.
1242 let state = node.state()?;
1206 let state = node.state()?;
1243 Ok(state.is_none() && node.descendants_with_entry_count() > 0)
1207 Ok(state.is_none() && node.descendants_with_entry_count() > 0)
1244 } else {
1208 } else {
1245 Ok(false)
1209 Ok(false)
1246 }
1210 }
1247 })
1211 })
1248 }
1212 }
1249
1213
1250 #[timed]
1214 #[timed]
1251 pub fn pack_v1(
1215 pub fn pack_v1(
1252 &self,
1216 &self,
1253 parents: DirstateParents,
1217 parents: DirstateParents,
1254 ) -> Result<Vec<u8>, DirstateError> {
1218 ) -> Result<Vec<u8>, DirstateError> {
1255 let map = self.get_map();
1219 let map = self.get_map();
1256 // Optizimation (to be measured?): pre-compute size to avoid `Vec`
1220 // Optizimation (to be measured?): pre-compute size to avoid `Vec`
1257 // reallocations
1221 // reallocations
1258 let mut size = parents.as_bytes().len();
1222 let mut size = parents.as_bytes().len();
1259 for node in map.iter_nodes() {
1223 for node in map.iter_nodes() {
1260 let node = node?;
1224 let node = node?;
1261 if node.entry()?.is_some() {
1225 if node.entry()?.is_some() {
1262 size += packed_entry_size(
1226 size += packed_entry_size(
1263 node.full_path(map.on_disk)?,
1227 node.full_path(map.on_disk)?,
1264 node.copy_source(map.on_disk)?,
1228 node.copy_source(map.on_disk)?,
1265 );
1229 );
1266 }
1230 }
1267 }
1231 }
1268
1232
1269 let mut packed = Vec::with_capacity(size);
1233 let mut packed = Vec::with_capacity(size);
1270 packed.extend(parents.as_bytes());
1234 packed.extend(parents.as_bytes());
1271
1235
1272 for node in map.iter_nodes() {
1236 for node in map.iter_nodes() {
1273 let node = node?;
1237 let node = node?;
1274 if let Some(entry) = node.entry()? {
1238 if let Some(entry) = node.entry()? {
1275 pack_entry(
1239 pack_entry(
1276 node.full_path(map.on_disk)?,
1240 node.full_path(map.on_disk)?,
1277 &entry,
1241 &entry,
1278 node.copy_source(map.on_disk)?,
1242 node.copy_source(map.on_disk)?,
1279 &mut packed,
1243 &mut packed,
1280 );
1244 );
1281 }
1245 }
1282 }
1246 }
1283 Ok(packed)
1247 Ok(packed)
1284 }
1248 }
1285
1249
1286 /// Returns new data and metadata together with whether that data should be
1250 /// Returns new data and metadata together with whether that data should be
1287 /// appended to the existing data file whose content is at
1251 /// appended to the existing data file whose content is at
1288 /// `map.on_disk` (true), instead of written to a new data file
1252 /// `map.on_disk` (true), instead of written to a new data file
1289 /// (false).
1253 /// (false).
1290 #[timed]
1254 #[timed]
1291 pub fn pack_v2(
1255 pub fn pack_v2(
1292 &self,
1256 &self,
1293 can_append: bool,
1257 can_append: bool,
1294 ) -> Result<(Vec<u8>, on_disk::TreeMetadata, bool), DirstateError> {
1258 ) -> Result<(Vec<u8>, on_disk::TreeMetadata, bool), DirstateError> {
1295 let map = self.get_map();
1259 let map = self.get_map();
1296 on_disk::write(map, can_append)
1260 on_disk::write(map, can_append)
1297 }
1261 }
1298
1262
1299 /// `callback` allows the caller to process and do something with the
1263 /// `callback` allows the caller to process and do something with the
1300 /// results of the status. This is needed to do so efficiently (i.e.
1264 /// results of the status. This is needed to do so efficiently (i.e.
1301 /// without cloning the `DirstateStatus` object with its paths) because
1265 /// without cloning the `DirstateStatus` object with its paths) because
1302 /// we need to borrow from `Self`.
1266 /// we need to borrow from `Self`.
1303 pub fn with_status<R>(
1267 pub fn with_status<R>(
1304 &mut self,
1268 &mut self,
1305 matcher: &(dyn Matcher + Sync),
1269 matcher: &(dyn Matcher + Sync),
1306 root_dir: PathBuf,
1270 root_dir: PathBuf,
1307 ignore_files: Vec<PathBuf>,
1271 ignore_files: Vec<PathBuf>,
1308 options: StatusOptions,
1272 options: StatusOptions,
1309 callback: impl for<'r> FnOnce(
1273 callback: impl for<'r> FnOnce(
1310 Result<(DirstateStatus<'r>, Vec<PatternFileWarning>), StatusError>,
1274 Result<(DirstateStatus<'r>, Vec<PatternFileWarning>), StatusError>,
1311 ) -> R,
1275 ) -> R,
1312 ) -> R {
1276 ) -> R {
1313 self.with_dmap_mut(|map| {
1277 self.with_dmap_mut(|map| {
1314 callback(super::status::status(
1278 callback(super::status::status(
1315 map,
1279 map,
1316 matcher,
1280 matcher,
1317 root_dir,
1281 root_dir,
1318 ignore_files,
1282 ignore_files,
1319 options,
1283 options,
1320 ))
1284 ))
1321 })
1285 })
1322 }
1286 }
1323
1287
1324 pub fn copy_map_len(&self) -> usize {
1288 pub fn copy_map_len(&self) -> usize {
1325 let map = self.get_map();
1289 let map = self.get_map();
1326 map.nodes_with_copy_source_count as usize
1290 map.nodes_with_copy_source_count as usize
1327 }
1291 }
1328
1292
1329 pub fn copy_map_iter(&self) -> CopyMapIter<'_> {
1293 pub fn copy_map_iter(&self) -> CopyMapIter<'_> {
1330 let map = self.get_map();
1294 let map = self.get_map();
1331 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1295 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1332 Ok(if let Some(source) = node.copy_source(map.on_disk)? {
1296 Ok(if let Some(source) = node.copy_source(map.on_disk)? {
1333 Some((node.full_path(map.on_disk)?, source))
1297 Some((node.full_path(map.on_disk)?, source))
1334 } else {
1298 } else {
1335 None
1299 None
1336 })
1300 })
1337 }))
1301 }))
1338 }
1302 }
1339
1303
1340 pub fn copy_map_contains_key(
1304 pub fn copy_map_contains_key(
1341 &self,
1305 &self,
1342 key: &HgPath,
1306 key: &HgPath,
1343 ) -> Result<bool, DirstateV2ParseError> {
1307 ) -> Result<bool, DirstateV2ParseError> {
1344 let map = self.get_map();
1308 let map = self.get_map();
1345 Ok(if let Some(node) = map.get_node(key)? {
1309 Ok(if let Some(node) = map.get_node(key)? {
1346 node.has_copy_source()
1310 node.has_copy_source()
1347 } else {
1311 } else {
1348 false
1312 false
1349 })
1313 })
1350 }
1314 }
1351
1315
1352 pub fn copy_map_get(
1316 pub fn copy_map_get(
1353 &self,
1317 &self,
1354 key: &HgPath,
1318 key: &HgPath,
1355 ) -> Result<Option<&HgPath>, DirstateV2ParseError> {
1319 ) -> Result<Option<&HgPath>, DirstateV2ParseError> {
1356 let map = self.get_map();
1320 let map = self.get_map();
1357 if let Some(node) = map.get_node(key)? {
1321 if let Some(node) = map.get_node(key)? {
1358 if let Some(source) = node.copy_source(map.on_disk)? {
1322 if let Some(source) = node.copy_source(map.on_disk)? {
1359 return Ok(Some(source));
1323 return Ok(Some(source));
1360 }
1324 }
1361 }
1325 }
1362 Ok(None)
1326 Ok(None)
1363 }
1327 }
1364
1328
1365 pub fn copy_map_remove(
1329 pub fn copy_map_remove(
1366 &mut self,
1330 &mut self,
1367 key: &HgPath,
1331 key: &HgPath,
1368 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1332 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1369 self.with_dmap_mut(|map| {
1333 self.with_dmap_mut(|map| {
1370 let count = &mut map.nodes_with_copy_source_count;
1334 let count = &mut map.nodes_with_copy_source_count;
1371 let unreachable_bytes = &mut map.unreachable_bytes;
1335 let unreachable_bytes = &mut map.unreachable_bytes;
1372 Ok(DirstateMap::get_node_mut(
1336 Ok(DirstateMap::get_node_mut(
1373 map.on_disk,
1337 map.on_disk,
1374 unreachable_bytes,
1338 unreachable_bytes,
1375 &mut map.root,
1339 &mut map.root,
1376 key,
1340 key,
1377 )?
1341 )?
1378 .and_then(|node| {
1342 .and_then(|node| {
1379 if let Some(source) = &node.copy_source {
1343 if let Some(source) = &node.copy_source {
1380 *count -= 1;
1344 *count -= 1;
1381 DirstateMap::count_dropped_path(unreachable_bytes, source);
1345 DirstateMap::count_dropped_path(unreachable_bytes, source);
1382 }
1346 }
1383 node.copy_source.take().map(Cow::into_owned)
1347 node.copy_source.take().map(Cow::into_owned)
1384 }))
1348 }))
1385 })
1349 })
1386 }
1350 }
1387
1351
1388 pub fn copy_map_insert(
1352 pub fn copy_map_insert(
1389 &mut self,
1353 &mut self,
1390 key: HgPathBuf,
1354 key: HgPathBuf,
1391 value: HgPathBuf,
1355 value: HgPathBuf,
1392 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1356 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1393 self.with_dmap_mut(|map| {
1357 self.with_dmap_mut(|map| {
1394 let node = DirstateMap::get_or_insert_node(
1358 let node = DirstateMap::get_or_insert_node(
1395 map.on_disk,
1359 map.on_disk,
1396 &mut map.unreachable_bytes,
1360 &mut map.unreachable_bytes,
1397 &mut map.root,
1361 &mut map.root,
1398 &key,
1362 &key,
1399 WithBasename::to_cow_owned,
1363 WithBasename::to_cow_owned,
1400 |_ancestor| {},
1364 |_ancestor| {},
1401 )?;
1365 )?;
1402 if node.copy_source.is_none() {
1366 if node.copy_source.is_none() {
1403 map.nodes_with_copy_source_count += 1
1367 map.nodes_with_copy_source_count += 1
1404 }
1368 }
1405 Ok(node.copy_source.replace(value.into()).map(Cow::into_owned))
1369 Ok(node.copy_source.replace(value.into()).map(Cow::into_owned))
1406 })
1370 })
1407 }
1371 }
1408
1372
1409 pub fn len(&self) -> usize {
1373 pub fn len(&self) -> usize {
1410 let map = self.get_map();
1374 let map = self.get_map();
1411 map.nodes_with_entry_count as usize
1375 map.nodes_with_entry_count as usize
1412 }
1376 }
1413
1377
1414 pub fn contains_key(
1378 pub fn contains_key(
1415 &self,
1379 &self,
1416 key: &HgPath,
1380 key: &HgPath,
1417 ) -> Result<bool, DirstateV2ParseError> {
1381 ) -> Result<bool, DirstateV2ParseError> {
1418 Ok(self.get(key)?.is_some())
1382 Ok(self.get(key)?.is_some())
1419 }
1383 }
1420
1384
1421 pub fn get(
1385 pub fn get(
1422 &self,
1386 &self,
1423 key: &HgPath,
1387 key: &HgPath,
1424 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
1388 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
1425 let map = self.get_map();
1389 let map = self.get_map();
1426 Ok(if let Some(node) = map.get_node(key)? {
1390 Ok(if let Some(node) = map.get_node(key)? {
1427 node.entry()?
1391 node.entry()?
1428 } else {
1392 } else {
1429 None
1393 None
1430 })
1394 })
1431 }
1395 }
1432
1396
1433 pub fn iter(&self) -> StateMapIter<'_> {
1397 pub fn iter(&self) -> StateMapIter<'_> {
1434 let map = self.get_map();
1398 let map = self.get_map();
1435 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1399 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1436 Ok(if let Some(entry) = node.entry()? {
1400 Ok(if let Some(entry) = node.entry()? {
1437 Some((node.full_path(map.on_disk)?, entry))
1401 Some((node.full_path(map.on_disk)?, entry))
1438 } else {
1402 } else {
1439 None
1403 None
1440 })
1404 })
1441 }))
1405 }))
1442 }
1406 }
1443
1407
1444 pub fn iter_tracked_dirs(
1408 pub fn iter_tracked_dirs(
1445 &mut self,
1409 &mut self,
1446 ) -> Result<
1410 ) -> Result<
1447 Box<
1411 Box<
1448 dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>>
1412 dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>>
1449 + Send
1413 + Send
1450 + '_,
1414 + '_,
1451 >,
1415 >,
1452 DirstateError,
1416 DirstateError,
1453 > {
1417 > {
1454 let map = self.get_map();
1418 let map = self.get_map();
1455 let on_disk = map.on_disk;
1419 let on_disk = map.on_disk;
1456 Ok(Box::new(filter_map_results(
1420 Ok(Box::new(filter_map_results(
1457 map.iter_nodes(),
1421 map.iter_nodes(),
1458 move |node| {
1422 move |node| {
1459 Ok(if node.tracked_descendants_count() > 0 {
1423 Ok(if node.tracked_descendants_count() > 0 {
1460 Some(node.full_path(on_disk)?)
1424 Some(node.full_path(on_disk)?)
1461 } else {
1425 } else {
1462 None
1426 None
1463 })
1427 })
1464 },
1428 },
1465 )))
1429 )))
1466 }
1430 }
1467
1431
1468 pub fn debug_iter(
1432 pub fn debug_iter(
1469 &self,
1433 &self,
1470 all: bool,
1434 all: bool,
1471 ) -> Box<
1435 ) -> Box<
1472 dyn Iterator<
1436 dyn Iterator<
1473 Item = Result<
1437 Item = Result<
1474 (&HgPath, (u8, i32, i32, i32)),
1438 (&HgPath, (u8, i32, i32, i32)),
1475 DirstateV2ParseError,
1439 DirstateV2ParseError,
1476 >,
1440 >,
1477 > + Send
1441 > + Send
1478 + '_,
1442 + '_,
1479 > {
1443 > {
1480 let map = self.get_map();
1444 let map = self.get_map();
1481 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1445 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1482 let debug_tuple = if let Some(entry) = node.entry()? {
1446 let debug_tuple = if let Some(entry) = node.entry()? {
1483 entry.debug_tuple()
1447 entry.debug_tuple()
1484 } else if !all {
1448 } else if !all {
1485 return Ok(None);
1449 return Ok(None);
1486 } else if let Some(mtime) = node.cached_directory_mtime()? {
1450 } else if let Some(mtime) = node.cached_directory_mtime()? {
1487 (b' ', 0, -1, mtime.truncated_seconds() as i32)
1451 (b' ', 0, -1, mtime.truncated_seconds() as i32)
1488 } else {
1452 } else {
1489 (b' ', 0, -1, -1)
1453 (b' ', 0, -1, -1)
1490 };
1454 };
1491 Ok(Some((node.full_path(map.on_disk)?, debug_tuple)))
1455 Ok(Some((node.full_path(map.on_disk)?, debug_tuple)))
1492 }))
1456 }))
1493 }
1457 }
1494 }
1458 }
@@ -1,592 +1,573 b''
1 // dirstate_map.rs
1 // dirstate_map.rs
2 //
2 //
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 //
4 //
5 // This software may be used and distributed according to the terms of the
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
6 // GNU General Public License version 2 or any later version.
7
7
8 //! Bindings for the `hg::dirstate::dirstate_map` file provided by the
8 //! Bindings for the `hg::dirstate::dirstate_map` file provided by the
9 //! `hg-core` package.
9 //! `hg-core` package.
10
10
11 use std::cell::{RefCell, RefMut};
11 use std::cell::{RefCell, RefMut};
12 use std::convert::TryInto;
12 use std::convert::TryInto;
13
13
14 use cpython::{
14 use cpython::{
15 exc, PyBool, PyBytes, PyClone, PyDict, PyErr, PyList, PyNone, PyObject,
15 exc, PyBool, PyBytes, PyClone, PyDict, PyErr, PyList, PyNone, PyObject,
16 PyResult, Python, PythonObject, ToPyObject, UnsafePyLeaked,
16 PyResult, Python, PythonObject, ToPyObject, UnsafePyLeaked,
17 };
17 };
18 use hg::dirstate::{ParentFileData, TruncatedTimestamp};
18 use hg::dirstate::{ParentFileData, TruncatedTimestamp};
19
19
20 use crate::{
20 use crate::{
21 dirstate::copymap::{CopyMap, CopyMapItemsIterator, CopyMapKeysIterator},
21 dirstate::copymap::{CopyMap, CopyMapItemsIterator, CopyMapKeysIterator},
22 dirstate::item::DirstateItem,
22 dirstate::item::DirstateItem,
23 pybytes_deref::PyBytesDeref,
23 pybytes_deref::PyBytesDeref,
24 };
24 };
25 use hg::{
25 use hg::{
26 dirstate::StateMapIter,
26 dirstate::StateMapIter,
27 dirstate_tree::on_disk::DirstateV2ParseError,
27 dirstate_tree::on_disk::DirstateV2ParseError,
28 dirstate_tree::owning::OwningDirstateMap,
28 dirstate_tree::owning::OwningDirstateMap,
29 revlog::Node,
29 revlog::Node,
30 utils::files::normalize_case,
30 utils::files::normalize_case,
31 utils::hg_path::{HgPath, HgPathBuf},
31 utils::hg_path::{HgPath, HgPathBuf},
32 DirstateEntry, DirstateError, DirstateParents, EntryState,
32 DirstateEntry, DirstateError, DirstateParents, EntryState,
33 };
33 };
34
34
35 // TODO
35 // TODO
36 // This object needs to share references to multiple members of its Rust
36 // This object needs to share references to multiple members of its Rust
37 // inner struct, namely `copy_map`, `dirs` and `all_dirs`.
37 // inner struct, namely `copy_map`, `dirs` and `all_dirs`.
38 // Right now `CopyMap` is done, but it needs to have an explicit reference
38 // Right now `CopyMap` is done, but it needs to have an explicit reference
39 // to `RustDirstateMap` which itself needs to have an encapsulation for
39 // to `RustDirstateMap` which itself needs to have an encapsulation for
40 // every method in `CopyMap` (copymapcopy, etc.).
40 // every method in `CopyMap` (copymapcopy, etc.).
41 // This is ugly and hard to maintain.
41 // This is ugly and hard to maintain.
42 // The same logic applies to `dirs` and `all_dirs`, however the `Dirs`
42 // The same logic applies to `dirs` and `all_dirs`, however the `Dirs`
43 // `py_class!` is already implemented and does not mention
43 // `py_class!` is already implemented and does not mention
44 // `RustDirstateMap`, rightfully so.
44 // `RustDirstateMap`, rightfully so.
45 // All attributes also have to have a separate refcount data attribute for
45 // All attributes also have to have a separate refcount data attribute for
46 // leaks, with all methods that go along for reference sharing.
46 // leaks, with all methods that go along for reference sharing.
47 py_class!(pub class DirstateMap |py| {
47 py_class!(pub class DirstateMap |py| {
48 @shared data inner: OwningDirstateMap;
48 @shared data inner: OwningDirstateMap;
49
49
50 /// Returns a `(dirstate_map, parents)` tuple
50 /// Returns a `(dirstate_map, parents)` tuple
51 @staticmethod
51 @staticmethod
52 def new_v1(
52 def new_v1(
53 on_disk: PyBytes,
53 on_disk: PyBytes,
54 ) -> PyResult<PyObject> {
54 ) -> PyResult<PyObject> {
55 let on_disk = PyBytesDeref::new(py, on_disk);
55 let on_disk = PyBytesDeref::new(py, on_disk);
56 let (map, parents) = OwningDirstateMap::new_v1(on_disk)
56 let (map, parents) = OwningDirstateMap::new_v1(on_disk)
57 .map_err(|e| dirstate_error(py, e))?;
57 .map_err(|e| dirstate_error(py, e))?;
58 let map = Self::create_instance(py, map)?;
58 let map = Self::create_instance(py, map)?;
59 let p1 = PyBytes::new(py, parents.p1.as_bytes());
59 let p1 = PyBytes::new(py, parents.p1.as_bytes());
60 let p2 = PyBytes::new(py, parents.p2.as_bytes());
60 let p2 = PyBytes::new(py, parents.p2.as_bytes());
61 let parents = (p1, p2);
61 let parents = (p1, p2);
62 Ok((map, parents).to_py_object(py).into_object())
62 Ok((map, parents).to_py_object(py).into_object())
63 }
63 }
64
64
65 /// Returns a DirstateMap
65 /// Returns a DirstateMap
66 @staticmethod
66 @staticmethod
67 def new_v2(
67 def new_v2(
68 on_disk: PyBytes,
68 on_disk: PyBytes,
69 data_size: usize,
69 data_size: usize,
70 tree_metadata: PyBytes,
70 tree_metadata: PyBytes,
71 ) -> PyResult<PyObject> {
71 ) -> PyResult<PyObject> {
72 let dirstate_error = |e: DirstateError| {
72 let dirstate_error = |e: DirstateError| {
73 PyErr::new::<exc::OSError, _>(py, format!("Dirstate error: {:?}", e))
73 PyErr::new::<exc::OSError, _>(py, format!("Dirstate error: {:?}", e))
74 };
74 };
75 let on_disk = PyBytesDeref::new(py, on_disk);
75 let on_disk = PyBytesDeref::new(py, on_disk);
76 let map = OwningDirstateMap::new_v2(
76 let map = OwningDirstateMap::new_v2(
77 on_disk, data_size, tree_metadata.data(py),
77 on_disk, data_size, tree_metadata.data(py),
78 ).map_err(dirstate_error)?;
78 ).map_err(dirstate_error)?;
79 let map = Self::create_instance(py, map)?;
79 let map = Self::create_instance(py, map)?;
80 Ok(map.into_object())
80 Ok(map.into_object())
81 }
81 }
82
82
83 def clear(&self) -> PyResult<PyObject> {
83 def clear(&self) -> PyResult<PyObject> {
84 self.inner(py).borrow_mut().clear();
84 self.inner(py).borrow_mut().clear();
85 Ok(py.None())
85 Ok(py.None())
86 }
86 }
87
87
88 def get(
88 def get(
89 &self,
89 &self,
90 key: PyObject,
90 key: PyObject,
91 default: Option<PyObject> = None
91 default: Option<PyObject> = None
92 ) -> PyResult<Option<PyObject>> {
92 ) -> PyResult<Option<PyObject>> {
93 let key = key.extract::<PyBytes>(py)?;
93 let key = key.extract::<PyBytes>(py)?;
94 match self
94 match self
95 .inner(py)
95 .inner(py)
96 .borrow()
96 .borrow()
97 .get(HgPath::new(key.data(py)))
97 .get(HgPath::new(key.data(py)))
98 .map_err(|e| v2_error(py, e))?
98 .map_err(|e| v2_error(py, e))?
99 {
99 {
100 Some(entry) => {
100 Some(entry) => {
101 Ok(Some(DirstateItem::new_as_pyobject(py, entry)?))
101 Ok(Some(DirstateItem::new_as_pyobject(py, entry)?))
102 },
102 },
103 None => Ok(default)
103 None => Ok(default)
104 }
104 }
105 }
105 }
106
106
107 def set_dirstate_item(
107 def set_dirstate_item(
108 &self,
108 &self,
109 path: PyObject,
109 path: PyObject,
110 item: DirstateItem
110 item: DirstateItem
111 ) -> PyResult<PyObject> {
111 ) -> PyResult<PyObject> {
112 let f = path.extract::<PyBytes>(py)?;
112 let f = path.extract::<PyBytes>(py)?;
113 let filename = HgPath::new(f.data(py));
113 let filename = HgPath::new(f.data(py));
114 self.inner(py)
114 self.inner(py)
115 .borrow_mut()
115 .borrow_mut()
116 .set_entry(filename, item.get_entry(py))
116 .set_entry(filename, item.get_entry(py))
117 .map_err(|e| v2_error(py, e))?;
117 .map_err(|e| v2_error(py, e))?;
118 Ok(py.None())
118 Ok(py.None())
119 }
119 }
120
120
121 def addfile(
121 def addfile(
122 &self,
122 &self,
123 f: PyBytes,
123 f: PyBytes,
124 item: DirstateItem,
124 item: DirstateItem,
125 ) -> PyResult<PyNone> {
125 ) -> PyResult<PyNone> {
126 let filename = HgPath::new(f.data(py));
126 let filename = HgPath::new(f.data(py));
127 let entry = item.get_entry(py);
127 let entry = item.get_entry(py);
128 self.inner(py)
128 self.inner(py)
129 .borrow_mut()
129 .borrow_mut()
130 .add_file(filename, entry)
130 .add_file(filename, entry)
131 .map_err(|e |dirstate_error(py, e))?;
131 .map_err(|e |dirstate_error(py, e))?;
132 Ok(PyNone)
132 Ok(PyNone)
133 }
133 }
134
134
135 def set_tracked(&self, f: PyObject) -> PyResult<PyBool> {
135 def set_tracked(&self, f: PyObject) -> PyResult<PyBool> {
136 let bytes = f.extract::<PyBytes>(py)?;
136 let bytes = f.extract::<PyBytes>(py)?;
137 let path = HgPath::new(bytes.data(py));
137 let path = HgPath::new(bytes.data(py));
138 let res = self.inner(py).borrow_mut().set_tracked(path);
138 let res = self.inner(py).borrow_mut().set_tracked(path);
139 let was_tracked = res.or_else(|_| {
139 let was_tracked = res.or_else(|_| {
140 Err(PyErr::new::<exc::OSError, _>(py, "Dirstate error".to_string()))
140 Err(PyErr::new::<exc::OSError, _>(py, "Dirstate error".to_string()))
141 })?;
141 })?;
142 Ok(was_tracked.to_py_object(py))
142 Ok(was_tracked.to_py_object(py))
143 }
143 }
144
144
145 def set_untracked(&self, f: PyObject) -> PyResult<PyBool> {
145 def set_untracked(&self, f: PyObject) -> PyResult<PyBool> {
146 let bytes = f.extract::<PyBytes>(py)?;
146 let bytes = f.extract::<PyBytes>(py)?;
147 let path = HgPath::new(bytes.data(py));
147 let path = HgPath::new(bytes.data(py));
148 let res = self.inner(py).borrow_mut().set_untracked(path);
148 let res = self.inner(py).borrow_mut().set_untracked(path);
149 let was_tracked = res.or_else(|_| {
149 let was_tracked = res.or_else(|_| {
150 Err(PyErr::new::<exc::OSError, _>(py, "Dirstate error".to_string()))
150 Err(PyErr::new::<exc::OSError, _>(py, "Dirstate error".to_string()))
151 })?;
151 })?;
152 Ok(was_tracked.to_py_object(py))
152 Ok(was_tracked.to_py_object(py))
153 }
153 }
154
154
155 def set_clean(
155 def set_clean(
156 &self,
156 &self,
157 f: PyObject,
157 f: PyObject,
158 mode: u32,
158 mode: u32,
159 size: u32,
159 size: u32,
160 mtime: (i64, u32, bool)
160 mtime: (i64, u32, bool)
161 ) -> PyResult<PyNone> {
161 ) -> PyResult<PyNone> {
162 let (mtime_s, mtime_ns, second_ambiguous) = mtime;
162 let (mtime_s, mtime_ns, second_ambiguous) = mtime;
163 let timestamp = TruncatedTimestamp::new_truncate(
163 let timestamp = TruncatedTimestamp::new_truncate(
164 mtime_s, mtime_ns, second_ambiguous
164 mtime_s, mtime_ns, second_ambiguous
165 );
165 );
166 let bytes = f.extract::<PyBytes>(py)?;
166 let bytes = f.extract::<PyBytes>(py)?;
167 let path = HgPath::new(bytes.data(py));
167 let path = HgPath::new(bytes.data(py));
168 let res = self.inner(py).borrow_mut().set_clean(
168 let res = self.inner(py).borrow_mut().set_clean(
169 path, mode, size, timestamp,
169 path, mode, size, timestamp,
170 );
170 );
171 res.or_else(|_| {
171 res.or_else(|_| {
172 Err(PyErr::new::<exc::OSError, _>(py, "Dirstate error".to_string()))
172 Err(PyErr::new::<exc::OSError, _>(py, "Dirstate error".to_string()))
173 })?;
173 })?;
174 Ok(PyNone)
174 Ok(PyNone)
175 }
175 }
176
176
177 def set_possibly_dirty(&self, f: PyObject) -> PyResult<PyNone> {
177 def set_possibly_dirty(&self, f: PyObject) -> PyResult<PyNone> {
178 let bytes = f.extract::<PyBytes>(py)?;
178 let bytes = f.extract::<PyBytes>(py)?;
179 let path = HgPath::new(bytes.data(py));
179 let path = HgPath::new(bytes.data(py));
180 let res = self.inner(py).borrow_mut().set_possibly_dirty(path);
180 let res = self.inner(py).borrow_mut().set_possibly_dirty(path);
181 res.or_else(|_| {
181 res.or_else(|_| {
182 Err(PyErr::new::<exc::OSError, _>(py, "Dirstate error".to_string()))
182 Err(PyErr::new::<exc::OSError, _>(py, "Dirstate error".to_string()))
183 })?;
183 })?;
184 Ok(PyNone)
184 Ok(PyNone)
185 }
185 }
186
186
187 def reset_state(
187 def reset_state(
188 &self,
188 &self,
189 f: PyObject,
189 f: PyObject,
190 wc_tracked: bool,
190 wc_tracked: bool,
191 p1_tracked: bool,
191 p1_tracked: bool,
192 p2_info: bool,
192 p2_info: bool,
193 has_meaningful_mtime: bool,
193 has_meaningful_mtime: bool,
194 parentfiledata: Option<(u32, u32, Option<(i64, u32, bool)>)>,
194 parentfiledata: Option<(u32, u32, Option<(i64, u32, bool)>)>,
195 ) -> PyResult<PyNone> {
195 ) -> PyResult<PyNone> {
196 let mut has_meaningful_mtime = has_meaningful_mtime;
196 let mut has_meaningful_mtime = has_meaningful_mtime;
197 let parent_file_data = match parentfiledata {
197 let parent_file_data = match parentfiledata {
198 None => {
198 None => {
199 has_meaningful_mtime = false;
199 has_meaningful_mtime = false;
200 None
200 None
201 },
201 },
202 Some(data) => {
202 Some(data) => {
203 let (mode, size, mtime_info) = data;
203 let (mode, size, mtime_info) = data;
204 let mtime = if let Some(mtime_info) = mtime_info {
204 let mtime = if let Some(mtime_info) = mtime_info {
205 let (mtime_s, mtime_ns, second_ambiguous) = mtime_info;
205 let (mtime_s, mtime_ns, second_ambiguous) = mtime_info;
206 let timestamp = TruncatedTimestamp::new_truncate(
206 let timestamp = TruncatedTimestamp::new_truncate(
207 mtime_s, mtime_ns, second_ambiguous
207 mtime_s, mtime_ns, second_ambiguous
208 );
208 );
209 Some(timestamp)
209 Some(timestamp)
210 } else {
210 } else {
211 has_meaningful_mtime = false;
211 has_meaningful_mtime = false;
212 None
212 None
213 };
213 };
214 Some(ParentFileData {
214 Some(ParentFileData {
215 mode_size: Some((mode, size)),
215 mode_size: Some((mode, size)),
216 mtime,
216 mtime,
217 })
217 })
218 }
218 }
219 };
219 };
220 let bytes = f.extract::<PyBytes>(py)?;
220 let bytes = f.extract::<PyBytes>(py)?;
221 let path = HgPath::new(bytes.data(py));
221 let path = HgPath::new(bytes.data(py));
222 let res = self.inner(py).borrow_mut().reset_state(
222 let res = self.inner(py).borrow_mut().reset_state(
223 path,
223 path,
224 wc_tracked,
224 wc_tracked,
225 p1_tracked,
225 p1_tracked,
226 p2_info,
226 p2_info,
227 has_meaningful_mtime,
227 has_meaningful_mtime,
228 parent_file_data,
228 parent_file_data,
229 );
229 );
230 res.or_else(|_| {
230 res.or_else(|_| {
231 Err(PyErr::new::<exc::OSError, _>(py, "Dirstate error".to_string()))
231 Err(PyErr::new::<exc::OSError, _>(py, "Dirstate error".to_string()))
232 })?;
232 })?;
233 Ok(PyNone)
233 Ok(PyNone)
234 }
234 }
235
235
236 def removefile(
237 &self,
238 f: PyObject,
239 in_merge: PyObject
240 ) -> PyResult<PyObject> {
241 self.inner(py).borrow_mut()
242 .remove_file(
243 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
244 in_merge.extract::<PyBool>(py)?.is_true(),
245 )
246 .or_else(|_| {
247 Err(PyErr::new::<exc::OSError, _>(
248 py,
249 "Dirstate error".to_string(),
250 ))
251 })?;
252 Ok(py.None())
253 }
254
255 def drop_item_and_copy_source(
236 def drop_item_and_copy_source(
256 &self,
237 &self,
257 f: PyBytes,
238 f: PyBytes,
258 ) -> PyResult<PyNone> {
239 ) -> PyResult<PyNone> {
259 self.inner(py)
240 self.inner(py)
260 .borrow_mut()
241 .borrow_mut()
261 .drop_entry_and_copy_source(HgPath::new(f.data(py)))
242 .drop_entry_and_copy_source(HgPath::new(f.data(py)))
262 .map_err(|e |dirstate_error(py, e))?;
243 .map_err(|e |dirstate_error(py, e))?;
263 Ok(PyNone)
244 Ok(PyNone)
264 }
245 }
265
246
266 def hastrackeddir(&self, d: PyObject) -> PyResult<PyBool> {
247 def hastrackeddir(&self, d: PyObject) -> PyResult<PyBool> {
267 let d = d.extract::<PyBytes>(py)?;
248 let d = d.extract::<PyBytes>(py)?;
268 Ok(self.inner(py).borrow_mut()
249 Ok(self.inner(py).borrow_mut()
269 .has_tracked_dir(HgPath::new(d.data(py)))
250 .has_tracked_dir(HgPath::new(d.data(py)))
270 .map_err(|e| {
251 .map_err(|e| {
271 PyErr::new::<exc::ValueError, _>(py, e.to_string())
252 PyErr::new::<exc::ValueError, _>(py, e.to_string())
272 })?
253 })?
273 .to_py_object(py))
254 .to_py_object(py))
274 }
255 }
275
256
276 def hasdir(&self, d: PyObject) -> PyResult<PyBool> {
257 def hasdir(&self, d: PyObject) -> PyResult<PyBool> {
277 let d = d.extract::<PyBytes>(py)?;
258 let d = d.extract::<PyBytes>(py)?;
278 Ok(self.inner(py).borrow_mut()
259 Ok(self.inner(py).borrow_mut()
279 .has_dir(HgPath::new(d.data(py)))
260 .has_dir(HgPath::new(d.data(py)))
280 .map_err(|e| {
261 .map_err(|e| {
281 PyErr::new::<exc::ValueError, _>(py, e.to_string())
262 PyErr::new::<exc::ValueError, _>(py, e.to_string())
282 })?
263 })?
283 .to_py_object(py))
264 .to_py_object(py))
284 }
265 }
285
266
286 def write_v1(
267 def write_v1(
287 &self,
268 &self,
288 p1: PyObject,
269 p1: PyObject,
289 p2: PyObject,
270 p2: PyObject,
290 ) -> PyResult<PyBytes> {
271 ) -> PyResult<PyBytes> {
291 let inner = self.inner(py).borrow();
272 let inner = self.inner(py).borrow();
292 let parents = DirstateParents {
273 let parents = DirstateParents {
293 p1: extract_node_id(py, &p1)?,
274 p1: extract_node_id(py, &p1)?,
294 p2: extract_node_id(py, &p2)?,
275 p2: extract_node_id(py, &p2)?,
295 };
276 };
296 let result = inner.pack_v1(parents);
277 let result = inner.pack_v1(parents);
297 match result {
278 match result {
298 Ok(packed) => Ok(PyBytes::new(py, &packed)),
279 Ok(packed) => Ok(PyBytes::new(py, &packed)),
299 Err(_) => Err(PyErr::new::<exc::OSError, _>(
280 Err(_) => Err(PyErr::new::<exc::OSError, _>(
300 py,
281 py,
301 "Dirstate error".to_string(),
282 "Dirstate error".to_string(),
302 )),
283 )),
303 }
284 }
304 }
285 }
305
286
306 /// Returns new data together with whether that data should be appended to
287 /// Returns new data together with whether that data should be appended to
307 /// the existing data file whose content is at `self.on_disk` (True),
288 /// the existing data file whose content is at `self.on_disk` (True),
308 /// instead of written to a new data file (False).
289 /// instead of written to a new data file (False).
309 def write_v2(
290 def write_v2(
310 &self,
291 &self,
311 can_append: bool,
292 can_append: bool,
312 ) -> PyResult<PyObject> {
293 ) -> PyResult<PyObject> {
313 let inner = self.inner(py).borrow();
294 let inner = self.inner(py).borrow();
314 let result = inner.pack_v2(can_append);
295 let result = inner.pack_v2(can_append);
315 match result {
296 match result {
316 Ok((packed, tree_metadata, append)) => {
297 Ok((packed, tree_metadata, append)) => {
317 let packed = PyBytes::new(py, &packed);
298 let packed = PyBytes::new(py, &packed);
318 let tree_metadata = PyBytes::new(py, tree_metadata.as_bytes());
299 let tree_metadata = PyBytes::new(py, tree_metadata.as_bytes());
319 let tuple = (packed, tree_metadata, append);
300 let tuple = (packed, tree_metadata, append);
320 Ok(tuple.to_py_object(py).into_object())
301 Ok(tuple.to_py_object(py).into_object())
321 },
302 },
322 Err(_) => Err(PyErr::new::<exc::OSError, _>(
303 Err(_) => Err(PyErr::new::<exc::OSError, _>(
323 py,
304 py,
324 "Dirstate error".to_string(),
305 "Dirstate error".to_string(),
325 )),
306 )),
326 }
307 }
327 }
308 }
328
309
329 def filefoldmapasdict(&self) -> PyResult<PyDict> {
310 def filefoldmapasdict(&self) -> PyResult<PyDict> {
330 let dict = PyDict::new(py);
311 let dict = PyDict::new(py);
331 for item in self.inner(py).borrow_mut().iter() {
312 for item in self.inner(py).borrow_mut().iter() {
332 let (path, entry) = item.map_err(|e| v2_error(py, e))?;
313 let (path, entry) = item.map_err(|e| v2_error(py, e))?;
333 if entry.state() != EntryState::Removed {
314 if entry.state() != EntryState::Removed {
334 let key = normalize_case(path);
315 let key = normalize_case(path);
335 let value = path;
316 let value = path;
336 dict.set_item(
317 dict.set_item(
337 py,
318 py,
338 PyBytes::new(py, key.as_bytes()).into_object(),
319 PyBytes::new(py, key.as_bytes()).into_object(),
339 PyBytes::new(py, value.as_bytes()).into_object(),
320 PyBytes::new(py, value.as_bytes()).into_object(),
340 )?;
321 )?;
341 }
322 }
342 }
323 }
343 Ok(dict)
324 Ok(dict)
344 }
325 }
345
326
346 def __len__(&self) -> PyResult<usize> {
327 def __len__(&self) -> PyResult<usize> {
347 Ok(self.inner(py).borrow().len())
328 Ok(self.inner(py).borrow().len())
348 }
329 }
349
330
350 def __contains__(&self, key: PyObject) -> PyResult<bool> {
331 def __contains__(&self, key: PyObject) -> PyResult<bool> {
351 let key = key.extract::<PyBytes>(py)?;
332 let key = key.extract::<PyBytes>(py)?;
352 self.inner(py)
333 self.inner(py)
353 .borrow()
334 .borrow()
354 .contains_key(HgPath::new(key.data(py)))
335 .contains_key(HgPath::new(key.data(py)))
355 .map_err(|e| v2_error(py, e))
336 .map_err(|e| v2_error(py, e))
356 }
337 }
357
338
358 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
339 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
359 let key = key.extract::<PyBytes>(py)?;
340 let key = key.extract::<PyBytes>(py)?;
360 let key = HgPath::new(key.data(py));
341 let key = HgPath::new(key.data(py));
361 match self
342 match self
362 .inner(py)
343 .inner(py)
363 .borrow()
344 .borrow()
364 .get(key)
345 .get(key)
365 .map_err(|e| v2_error(py, e))?
346 .map_err(|e| v2_error(py, e))?
366 {
347 {
367 Some(entry) => {
348 Some(entry) => {
368 Ok(DirstateItem::new_as_pyobject(py, entry)?)
349 Ok(DirstateItem::new_as_pyobject(py, entry)?)
369 },
350 },
370 None => Err(PyErr::new::<exc::KeyError, _>(
351 None => Err(PyErr::new::<exc::KeyError, _>(
371 py,
352 py,
372 String::from_utf8_lossy(key.as_bytes()),
353 String::from_utf8_lossy(key.as_bytes()),
373 )),
354 )),
374 }
355 }
375 }
356 }
376
357
377 def keys(&self) -> PyResult<DirstateMapKeysIterator> {
358 def keys(&self) -> PyResult<DirstateMapKeysIterator> {
378 let leaked_ref = self.inner(py).leak_immutable();
359 let leaked_ref = self.inner(py).leak_immutable();
379 DirstateMapKeysIterator::from_inner(
360 DirstateMapKeysIterator::from_inner(
380 py,
361 py,
381 unsafe { leaked_ref.map(py, |o| o.iter()) },
362 unsafe { leaked_ref.map(py, |o| o.iter()) },
382 )
363 )
383 }
364 }
384
365
385 def items(&self) -> PyResult<DirstateMapItemsIterator> {
366 def items(&self) -> PyResult<DirstateMapItemsIterator> {
386 let leaked_ref = self.inner(py).leak_immutable();
367 let leaked_ref = self.inner(py).leak_immutable();
387 DirstateMapItemsIterator::from_inner(
368 DirstateMapItemsIterator::from_inner(
388 py,
369 py,
389 unsafe { leaked_ref.map(py, |o| o.iter()) },
370 unsafe { leaked_ref.map(py, |o| o.iter()) },
390 )
371 )
391 }
372 }
392
373
393 def __iter__(&self) -> PyResult<DirstateMapKeysIterator> {
374 def __iter__(&self) -> PyResult<DirstateMapKeysIterator> {
394 let leaked_ref = self.inner(py).leak_immutable();
375 let leaked_ref = self.inner(py).leak_immutable();
395 DirstateMapKeysIterator::from_inner(
376 DirstateMapKeysIterator::from_inner(
396 py,
377 py,
397 unsafe { leaked_ref.map(py, |o| o.iter()) },
378 unsafe { leaked_ref.map(py, |o| o.iter()) },
398 )
379 )
399 }
380 }
400
381
401 // TODO all copymap* methods, see docstring above
382 // TODO all copymap* methods, see docstring above
402 def copymapcopy(&self) -> PyResult<PyDict> {
383 def copymapcopy(&self) -> PyResult<PyDict> {
403 let dict = PyDict::new(py);
384 let dict = PyDict::new(py);
404 for item in self.inner(py).borrow().copy_map_iter() {
385 for item in self.inner(py).borrow().copy_map_iter() {
405 let (key, value) = item.map_err(|e| v2_error(py, e))?;
386 let (key, value) = item.map_err(|e| v2_error(py, e))?;
406 dict.set_item(
387 dict.set_item(
407 py,
388 py,
408 PyBytes::new(py, key.as_bytes()),
389 PyBytes::new(py, key.as_bytes()),
409 PyBytes::new(py, value.as_bytes()),
390 PyBytes::new(py, value.as_bytes()),
410 )?;
391 )?;
411 }
392 }
412 Ok(dict)
393 Ok(dict)
413 }
394 }
414
395
415 def copymapgetitem(&self, key: PyObject) -> PyResult<PyBytes> {
396 def copymapgetitem(&self, key: PyObject) -> PyResult<PyBytes> {
416 let key = key.extract::<PyBytes>(py)?;
397 let key = key.extract::<PyBytes>(py)?;
417 match self
398 match self
418 .inner(py)
399 .inner(py)
419 .borrow()
400 .borrow()
420 .copy_map_get(HgPath::new(key.data(py)))
401 .copy_map_get(HgPath::new(key.data(py)))
421 .map_err(|e| v2_error(py, e))?
402 .map_err(|e| v2_error(py, e))?
422 {
403 {
423 Some(copy) => Ok(PyBytes::new(py, copy.as_bytes())),
404 Some(copy) => Ok(PyBytes::new(py, copy.as_bytes())),
424 None => Err(PyErr::new::<exc::KeyError, _>(
405 None => Err(PyErr::new::<exc::KeyError, _>(
425 py,
406 py,
426 String::from_utf8_lossy(key.data(py)),
407 String::from_utf8_lossy(key.data(py)),
427 )),
408 )),
428 }
409 }
429 }
410 }
430 def copymap(&self) -> PyResult<CopyMap> {
411 def copymap(&self) -> PyResult<CopyMap> {
431 CopyMap::from_inner(py, self.clone_ref(py))
412 CopyMap::from_inner(py, self.clone_ref(py))
432 }
413 }
433
414
434 def copymaplen(&self) -> PyResult<usize> {
415 def copymaplen(&self) -> PyResult<usize> {
435 Ok(self.inner(py).borrow().copy_map_len())
416 Ok(self.inner(py).borrow().copy_map_len())
436 }
417 }
437 def copymapcontains(&self, key: PyObject) -> PyResult<bool> {
418 def copymapcontains(&self, key: PyObject) -> PyResult<bool> {
438 let key = key.extract::<PyBytes>(py)?;
419 let key = key.extract::<PyBytes>(py)?;
439 self.inner(py)
420 self.inner(py)
440 .borrow()
421 .borrow()
441 .copy_map_contains_key(HgPath::new(key.data(py)))
422 .copy_map_contains_key(HgPath::new(key.data(py)))
442 .map_err(|e| v2_error(py, e))
423 .map_err(|e| v2_error(py, e))
443 }
424 }
444 def copymapget(
425 def copymapget(
445 &self,
426 &self,
446 key: PyObject,
427 key: PyObject,
447 default: Option<PyObject>
428 default: Option<PyObject>
448 ) -> PyResult<Option<PyObject>> {
429 ) -> PyResult<Option<PyObject>> {
449 let key = key.extract::<PyBytes>(py)?;
430 let key = key.extract::<PyBytes>(py)?;
450 match self
431 match self
451 .inner(py)
432 .inner(py)
452 .borrow()
433 .borrow()
453 .copy_map_get(HgPath::new(key.data(py)))
434 .copy_map_get(HgPath::new(key.data(py)))
454 .map_err(|e| v2_error(py, e))?
435 .map_err(|e| v2_error(py, e))?
455 {
436 {
456 Some(copy) => Ok(Some(
437 Some(copy) => Ok(Some(
457 PyBytes::new(py, copy.as_bytes()).into_object(),
438 PyBytes::new(py, copy.as_bytes()).into_object(),
458 )),
439 )),
459 None => Ok(default),
440 None => Ok(default),
460 }
441 }
461 }
442 }
462 def copymapsetitem(
443 def copymapsetitem(
463 &self,
444 &self,
464 key: PyObject,
445 key: PyObject,
465 value: PyObject
446 value: PyObject
466 ) -> PyResult<PyObject> {
447 ) -> PyResult<PyObject> {
467 let key = key.extract::<PyBytes>(py)?;
448 let key = key.extract::<PyBytes>(py)?;
468 let value = value.extract::<PyBytes>(py)?;
449 let value = value.extract::<PyBytes>(py)?;
469 self.inner(py)
450 self.inner(py)
470 .borrow_mut()
451 .borrow_mut()
471 .copy_map_insert(
452 .copy_map_insert(
472 HgPathBuf::from_bytes(key.data(py)),
453 HgPathBuf::from_bytes(key.data(py)),
473 HgPathBuf::from_bytes(value.data(py)),
454 HgPathBuf::from_bytes(value.data(py)),
474 )
455 )
475 .map_err(|e| v2_error(py, e))?;
456 .map_err(|e| v2_error(py, e))?;
476 Ok(py.None())
457 Ok(py.None())
477 }
458 }
478 def copymappop(
459 def copymappop(
479 &self,
460 &self,
480 key: PyObject,
461 key: PyObject,
481 default: Option<PyObject>
462 default: Option<PyObject>
482 ) -> PyResult<Option<PyObject>> {
463 ) -> PyResult<Option<PyObject>> {
483 let key = key.extract::<PyBytes>(py)?;
464 let key = key.extract::<PyBytes>(py)?;
484 match self
465 match self
485 .inner(py)
466 .inner(py)
486 .borrow_mut()
467 .borrow_mut()
487 .copy_map_remove(HgPath::new(key.data(py)))
468 .copy_map_remove(HgPath::new(key.data(py)))
488 .map_err(|e| v2_error(py, e))?
469 .map_err(|e| v2_error(py, e))?
489 {
470 {
490 Some(copy) => Ok(Some(
471 Some(copy) => Ok(Some(
491 PyBytes::new(py, copy.as_bytes()).into_object(),
472 PyBytes::new(py, copy.as_bytes()).into_object(),
492 )),
473 )),
493 None => Ok(default),
474 None => Ok(default),
494 }
475 }
495 }
476 }
496
477
497 def copymapiter(&self) -> PyResult<CopyMapKeysIterator> {
478 def copymapiter(&self) -> PyResult<CopyMapKeysIterator> {
498 let leaked_ref = self.inner(py).leak_immutable();
479 let leaked_ref = self.inner(py).leak_immutable();
499 CopyMapKeysIterator::from_inner(
480 CopyMapKeysIterator::from_inner(
500 py,
481 py,
501 unsafe { leaked_ref.map(py, |o| o.copy_map_iter()) },
482 unsafe { leaked_ref.map(py, |o| o.copy_map_iter()) },
502 )
483 )
503 }
484 }
504
485
505 def copymapitemsiter(&self) -> PyResult<CopyMapItemsIterator> {
486 def copymapitemsiter(&self) -> PyResult<CopyMapItemsIterator> {
506 let leaked_ref = self.inner(py).leak_immutable();
487 let leaked_ref = self.inner(py).leak_immutable();
507 CopyMapItemsIterator::from_inner(
488 CopyMapItemsIterator::from_inner(
508 py,
489 py,
509 unsafe { leaked_ref.map(py, |o| o.copy_map_iter()) },
490 unsafe { leaked_ref.map(py, |o| o.copy_map_iter()) },
510 )
491 )
511 }
492 }
512
493
513 def tracked_dirs(&self) -> PyResult<PyList> {
494 def tracked_dirs(&self) -> PyResult<PyList> {
514 let dirs = PyList::new(py, &[]);
495 let dirs = PyList::new(py, &[]);
515 for path in self.inner(py).borrow_mut().iter_tracked_dirs()
496 for path in self.inner(py).borrow_mut().iter_tracked_dirs()
516 .map_err(|e |dirstate_error(py, e))?
497 .map_err(|e |dirstate_error(py, e))?
517 {
498 {
518 let path = path.map_err(|e| v2_error(py, e))?;
499 let path = path.map_err(|e| v2_error(py, e))?;
519 let path = PyBytes::new(py, path.as_bytes());
500 let path = PyBytes::new(py, path.as_bytes());
520 dirs.append(py, path.into_object())
501 dirs.append(py, path.into_object())
521 }
502 }
522 Ok(dirs)
503 Ok(dirs)
523 }
504 }
524
505
525 def debug_iter(&self, all: bool) -> PyResult<PyList> {
506 def debug_iter(&self, all: bool) -> PyResult<PyList> {
526 let dirs = PyList::new(py, &[]);
507 let dirs = PyList::new(py, &[]);
527 for item in self.inner(py).borrow().debug_iter(all) {
508 for item in self.inner(py).borrow().debug_iter(all) {
528 let (path, (state, mode, size, mtime)) =
509 let (path, (state, mode, size, mtime)) =
529 item.map_err(|e| v2_error(py, e))?;
510 item.map_err(|e| v2_error(py, e))?;
530 let path = PyBytes::new(py, path.as_bytes());
511 let path = PyBytes::new(py, path.as_bytes());
531 let item = (path, state, mode, size, mtime);
512 let item = (path, state, mode, size, mtime);
532 dirs.append(py, item.to_py_object(py).into_object())
513 dirs.append(py, item.to_py_object(py).into_object())
533 }
514 }
534 Ok(dirs)
515 Ok(dirs)
535 }
516 }
536 });
517 });
537
518
538 impl DirstateMap {
519 impl DirstateMap {
539 pub fn get_inner_mut<'a>(
520 pub fn get_inner_mut<'a>(
540 &'a self,
521 &'a self,
541 py: Python<'a>,
522 py: Python<'a>,
542 ) -> RefMut<'a, OwningDirstateMap> {
523 ) -> RefMut<'a, OwningDirstateMap> {
543 self.inner(py).borrow_mut()
524 self.inner(py).borrow_mut()
544 }
525 }
545 fn translate_key(
526 fn translate_key(
546 py: Python,
527 py: Python,
547 res: Result<(&HgPath, DirstateEntry), DirstateV2ParseError>,
528 res: Result<(&HgPath, DirstateEntry), DirstateV2ParseError>,
548 ) -> PyResult<Option<PyBytes>> {
529 ) -> PyResult<Option<PyBytes>> {
549 let (f, _entry) = res.map_err(|e| v2_error(py, e))?;
530 let (f, _entry) = res.map_err(|e| v2_error(py, e))?;
550 Ok(Some(PyBytes::new(py, f.as_bytes())))
531 Ok(Some(PyBytes::new(py, f.as_bytes())))
551 }
532 }
552 fn translate_key_value(
533 fn translate_key_value(
553 py: Python,
534 py: Python,
554 res: Result<(&HgPath, DirstateEntry), DirstateV2ParseError>,
535 res: Result<(&HgPath, DirstateEntry), DirstateV2ParseError>,
555 ) -> PyResult<Option<(PyBytes, PyObject)>> {
536 ) -> PyResult<Option<(PyBytes, PyObject)>> {
556 let (f, entry) = res.map_err(|e| v2_error(py, e))?;
537 let (f, entry) = res.map_err(|e| v2_error(py, e))?;
557 Ok(Some((
538 Ok(Some((
558 PyBytes::new(py, f.as_bytes()),
539 PyBytes::new(py, f.as_bytes()),
559 DirstateItem::new_as_pyobject(py, entry)?,
540 DirstateItem::new_as_pyobject(py, entry)?,
560 )))
541 )))
561 }
542 }
562 }
543 }
563
544
564 py_shared_iterator!(
545 py_shared_iterator!(
565 DirstateMapKeysIterator,
546 DirstateMapKeysIterator,
566 UnsafePyLeaked<StateMapIter<'static>>,
547 UnsafePyLeaked<StateMapIter<'static>>,
567 DirstateMap::translate_key,
548 DirstateMap::translate_key,
568 Option<PyBytes>
549 Option<PyBytes>
569 );
550 );
570
551
571 py_shared_iterator!(
552 py_shared_iterator!(
572 DirstateMapItemsIterator,
553 DirstateMapItemsIterator,
573 UnsafePyLeaked<StateMapIter<'static>>,
554 UnsafePyLeaked<StateMapIter<'static>>,
574 DirstateMap::translate_key_value,
555 DirstateMap::translate_key_value,
575 Option<(PyBytes, PyObject)>
556 Option<(PyBytes, PyObject)>
576 );
557 );
577
558
578 fn extract_node_id(py: Python, obj: &PyObject) -> PyResult<Node> {
559 fn extract_node_id(py: Python, obj: &PyObject) -> PyResult<Node> {
579 let bytes = obj.extract::<PyBytes>(py)?;
560 let bytes = obj.extract::<PyBytes>(py)?;
580 match bytes.data(py).try_into() {
561 match bytes.data(py).try_into() {
581 Ok(s) => Ok(s),
562 Ok(s) => Ok(s),
582 Err(e) => Err(PyErr::new::<exc::ValueError, _>(py, e.to_string())),
563 Err(e) => Err(PyErr::new::<exc::ValueError, _>(py, e.to_string())),
583 }
564 }
584 }
565 }
585
566
586 pub(super) fn v2_error(py: Python<'_>, _: DirstateV2ParseError) -> PyErr {
567 pub(super) fn v2_error(py: Python<'_>, _: DirstateV2ParseError) -> PyErr {
587 PyErr::new::<exc::ValueError, _>(py, "corrupted dirstate-v2")
568 PyErr::new::<exc::ValueError, _>(py, "corrupted dirstate-v2")
588 }
569 }
589
570
590 fn dirstate_error(py: Python<'_>, e: DirstateError) -> PyErr {
571 fn dirstate_error(py: Python<'_>, e: DirstateError) -> PyErr {
591 PyErr::new::<exc::OSError, _>(py, format!("Dirstate error: {:?}", e))
572 PyErr::new::<exc::OSError, _>(py, format!("Dirstate error: {:?}", e))
592 }
573 }
General Comments 0
You need to be logged in to leave comments. Login now