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