##// END OF EJS Templates
rust-utils: use new find_dirs iterator...
Raphaël Gomès -
r42814:717686c5 default
parent child Browse files
Show More
@@ -1,360 +1,329 b''
1 // dirs_multiset.rs
1 // dirs_multiset.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 //! A multiset of directory names.
8 //! A multiset of directory names.
9 //!
9 //!
10 //! Used to counts the references to directories in a manifest or dirstate.
10 //! Used to counts the references to directories in a manifest or dirstate.
11 use std::collections::hash_map::{Entry, Iter};
11 use std::collections::hash_map::{Entry, Iter};
12 use std::collections::HashMap;
12 use std::collections::HashMap;
13 use {DirsIterable, DirstateEntry, DirstateMapError};
13 use {DirsIterable, DirstateEntry, DirstateMapError};
14 use utils::files;
14
15
15 #[derive(PartialEq, Debug)]
16 #[derive(PartialEq, Debug)]
16 pub struct DirsMultiset {
17 pub struct DirsMultiset {
17 inner: HashMap<Vec<u8>, u32>,
18 inner: HashMap<Vec<u8>, u32>,
18 }
19 }
19
20
20 impl DirsMultiset {
21 impl DirsMultiset {
21 /// Initializes the multiset from a dirstate or a manifest.
22 /// Initializes the multiset from a dirstate or a manifest.
22 ///
23 ///
23 /// If `skip_state` is provided, skips dirstate entries with equal state.
24 /// If `skip_state` is provided, skips dirstate entries with equal state.
24 pub fn new(iterable: DirsIterable, skip_state: Option<i8>) -> Self {
25 pub fn new(iterable: DirsIterable, skip_state: Option<i8>) -> Self {
25 let mut multiset = DirsMultiset {
26 let mut multiset = DirsMultiset {
26 inner: HashMap::new(),
27 inner: HashMap::new(),
27 };
28 };
28
29
29 match iterable {
30 match iterable {
30 DirsIterable::Dirstate(vec) => {
31 DirsIterable::Dirstate(vec) => {
31 for (ref filename, DirstateEntry { state, .. }) in vec {
32 for (ref filename, DirstateEntry { state, .. }) in vec {
32 // This `if` is optimized out of the loop
33 // This `if` is optimized out of the loop
33 if let Some(skip) = skip_state {
34 if let Some(skip) = skip_state {
34 if skip != state {
35 if skip != state {
35 multiset.add_path(filename);
36 multiset.add_path(filename);
36 }
37 }
37 } else {
38 } else {
38 multiset.add_path(filename);
39 multiset.add_path(filename);
39 }
40 }
40 }
41 }
41 }
42 }
42 DirsIterable::Manifest(vec) => {
43 DirsIterable::Manifest(vec) => {
43 for ref filename in vec {
44 for ref filename in vec {
44 multiset.add_path(filename);
45 multiset.add_path(filename);
45 }
46 }
46 }
47 }
47 }
48 }
48
49
49 multiset
50 multiset
50 }
51 }
51
52
52 /// Returns the slice up to the next directory name from right to left,
53 /// without trailing slash
54 fn find_dir(path: &[u8]) -> &[u8] {
55 let mut path = path;
56 loop {
57 if let Some(new_pos) = path.len().checked_sub(1) {
58 if path[new_pos] == b'/' {
59 break &path[..new_pos];
60 }
61 path = &path[..new_pos];
62 } else {
63 break &[];
64 }
65 }
66 }
67
68 /// Increases the count of deepest directory contained in the path.
53 /// Increases the count of deepest directory contained in the path.
69 ///
54 ///
70 /// If the directory is not yet in the map, adds its parents.
55 /// If the directory is not yet in the map, adds its parents.
71 pub fn add_path(&mut self, path: &[u8]) {
56 pub fn add_path(&mut self, path: &[u8]) {
72 let mut pos = path.len();
57 for subpath in files::find_dirs(path) {
73
74 loop {
75 let subpath = Self::find_dir(&path[..pos]);
76 if let Some(val) = self.inner.get_mut(subpath) {
58 if let Some(val) = self.inner.get_mut(subpath) {
77 *val += 1;
59 *val += 1;
78 break;
60 break;
79 }
61 }
80 self.inner.insert(subpath.to_owned(), 1);
62 self.inner.insert(subpath.to_owned(), 1);
81
82 pos = subpath.len();
83 if pos == 0 {
84 break;
85 }
86 }
63 }
87 }
64 }
88
65
89 /// Decreases the count of deepest directory contained in the path.
66 /// Decreases the count of deepest directory contained in the path.
90 ///
67 ///
91 /// If it is the only reference, decreases all parents until one is
68 /// If it is the only reference, decreases all parents until one is
92 /// removed.
69 /// removed.
93 /// If the directory is not in the map, something horrible has happened.
70 /// If the directory is not in the map, something horrible has happened.
94 pub fn delete_path(
71 pub fn delete_path(
95 &mut self,
72 &mut self,
96 path: &[u8],
73 path: &[u8],
97 ) -> Result<(), DirstateMapError> {
74 ) -> Result<(), DirstateMapError> {
98 let mut pos = path.len();
75 for subpath in files::find_dirs(path) {
99
100 loop {
101 let subpath = Self::find_dir(&path[..pos]);
102 match self.inner.entry(subpath.to_owned()) {
76 match self.inner.entry(subpath.to_owned()) {
103 Entry::Occupied(mut entry) => {
77 Entry::Occupied(mut entry) => {
104 let val = entry.get().clone();
78 let val = entry.get().clone();
105 if val > 1 {
79 if val > 1 {
106 entry.insert(val - 1);
80 entry.insert(val - 1);
107 break;
81 break;
108 }
82 }
109 entry.remove();
83 entry.remove();
110 }
84 }
111 Entry::Vacant(_) => {
85 Entry::Vacant(_) => {
112 return Err(DirstateMapError::PathNotFound(
86 return Err(DirstateMapError::PathNotFound(
113 path.to_owned(),
87 path.to_owned(),
114 ))
88 ))
115 }
89 }
116 };
90 };
117
118 pos = subpath.len();
119 if pos == 0 {
120 break;
121 }
122 }
91 }
123
92
124 Ok(())
93 Ok(())
125 }
94 }
126
95
127 pub fn contains_key(&self, key: &[u8]) -> bool {
96 pub fn contains_key(&self, key: &[u8]) -> bool {
128 self.inner.contains_key(key)
97 self.inner.contains_key(key)
129 }
98 }
130
99
131 pub fn iter(&self) -> Iter<Vec<u8>, u32> {
100 pub fn iter(&self) -> Iter<Vec<u8>, u32> {
132 self.inner.iter()
101 self.inner.iter()
133 }
102 }
134
103
135 pub fn len(&self) -> usize {
104 pub fn len(&self) -> usize {
136 self.inner.len()
105 self.inner.len()
137 }
106 }
138 }
107 }
139
108
140 #[cfg(test)]
109 #[cfg(test)]
141 mod tests {
110 mod tests {
142 use super::*;
111 use super::*;
143
112
144 #[test]
113 #[test]
145 fn test_delete_path_path_not_found() {
114 fn test_delete_path_path_not_found() {
146 let mut map = DirsMultiset::new(DirsIterable::Manifest(vec![]), None);
115 let mut map = DirsMultiset::new(DirsIterable::Manifest(vec![]), None);
147 let path = b"doesnotexist/";
116 let path = b"doesnotexist/";
148 assert_eq!(
117 assert_eq!(
149 Err(DirstateMapError::PathNotFound(path.to_vec())),
118 Err(DirstateMapError::PathNotFound(path.to_vec())),
150 map.delete_path(path)
119 map.delete_path(path)
151 );
120 );
152 }
121 }
153
122
154 #[test]
123 #[test]
155 fn test_delete_path_empty_path() {
124 fn test_delete_path_empty_path() {
156 let mut map =
125 let mut map =
157 DirsMultiset::new(DirsIterable::Manifest(vec![vec![]]), None);
126 DirsMultiset::new(DirsIterable::Manifest(vec![vec![]]), None);
158 let path = b"";
127 let path = b"";
159 assert_eq!(Ok(()), map.delete_path(path));
128 assert_eq!(Ok(()), map.delete_path(path));
160 assert_eq!(
129 assert_eq!(
161 Err(DirstateMapError::PathNotFound(path.to_vec())),
130 Err(DirstateMapError::PathNotFound(path.to_vec())),
162 map.delete_path(path)
131 map.delete_path(path)
163 );
132 );
164 }
133 }
165
134
166 #[test]
135 #[test]
167 fn test_delete_path_successful() {
136 fn test_delete_path_successful() {
168 let mut map = DirsMultiset {
137 let mut map = DirsMultiset {
169 inner: [("", 5), ("a", 3), ("a/b", 2), ("a/c", 1)]
138 inner: [("", 5), ("a", 3), ("a/b", 2), ("a/c", 1)]
170 .iter()
139 .iter()
171 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
140 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
172 .collect(),
141 .collect(),
173 };
142 };
174
143
175 assert_eq!(Ok(()), map.delete_path(b"a/b/"));
144 assert_eq!(Ok(()), map.delete_path(b"a/b/"));
176 assert_eq!(Ok(()), map.delete_path(b"a/b/"));
145 assert_eq!(Ok(()), map.delete_path(b"a/b/"));
177 assert_eq!(
146 assert_eq!(
178 Err(DirstateMapError::PathNotFound(b"a/b/".to_vec())),
147 Err(DirstateMapError::PathNotFound(b"a/b/".to_vec())),
179 map.delete_path(b"a/b/")
148 map.delete_path(b"a/b/")
180 );
149 );
181
150
182 assert_eq!(2, *map.inner.get(&b"a".to_vec()).unwrap());
151 assert_eq!(2, *map.inner.get(&b"a".to_vec()).unwrap());
183 assert_eq!(1, *map.inner.get(&b"a/c".to_vec()).unwrap());
152 assert_eq!(1, *map.inner.get(&b"a/c".to_vec()).unwrap());
184 eprintln!("{:?}", map);
153 eprintln!("{:?}", map);
185 assert_eq!(Ok(()), map.delete_path(b"a/"));
154 assert_eq!(Ok(()), map.delete_path(b"a/"));
186 eprintln!("{:?}", map);
155 eprintln!("{:?}", map);
187
156
188 assert_eq!(Ok(()), map.delete_path(b"a/c/"));
157 assert_eq!(Ok(()), map.delete_path(b"a/c/"));
189 assert_eq!(
158 assert_eq!(
190 Err(DirstateMapError::PathNotFound(b"a/c/".to_vec())),
159 Err(DirstateMapError::PathNotFound(b"a/c/".to_vec())),
191 map.delete_path(b"a/c/")
160 map.delete_path(b"a/c/")
192 );
161 );
193 }
162 }
194
163
195 #[test]
164 #[test]
196 fn test_add_path_empty_path() {
165 fn test_add_path_empty_path() {
197 let mut map = DirsMultiset::new(DirsIterable::Manifest(vec![]), None);
166 let mut map = DirsMultiset::new(DirsIterable::Manifest(vec![]), None);
198 let path = b"";
167 let path = b"";
199 map.add_path(path);
168 map.add_path(path);
200
169
201 assert_eq!(1, map.len());
170 assert_eq!(1, map.len());
202 }
171 }
203
172
204 #[test]
173 #[test]
205 fn test_add_path_successful() {
174 fn test_add_path_successful() {
206 let mut map = DirsMultiset::new(DirsIterable::Manifest(vec![]), None);
175 let mut map = DirsMultiset::new(DirsIterable::Manifest(vec![]), None);
207
176
208 map.add_path(b"a/");
177 map.add_path(b"a/");
209 assert_eq!(1, *map.inner.get(&b"a".to_vec()).unwrap());
178 assert_eq!(1, *map.inner.get(&b"a".to_vec()).unwrap());
210 assert_eq!(1, *map.inner.get(&Vec::new()).unwrap());
179 assert_eq!(1, *map.inner.get(&Vec::new()).unwrap());
211 assert_eq!(2, map.len());
180 assert_eq!(2, map.len());
212
181
213 // Non directory should be ignored
182 // Non directory should be ignored
214 map.add_path(b"a");
183 map.add_path(b"a");
215 assert_eq!(1, *map.inner.get(&b"a".to_vec()).unwrap());
184 assert_eq!(1, *map.inner.get(&b"a".to_vec()).unwrap());
216 assert_eq!(2, map.len());
185 assert_eq!(2, map.len());
217
186
218 // Non directory will still add its base
187 // Non directory will still add its base
219 map.add_path(b"a/b");
188 map.add_path(b"a/b");
220 assert_eq!(2, *map.inner.get(&b"a".to_vec()).unwrap());
189 assert_eq!(2, *map.inner.get(&b"a".to_vec()).unwrap());
221 assert_eq!(2, map.len());
190 assert_eq!(2, map.len());
222
191
223 // Duplicate path works
192 // Duplicate path works
224 map.add_path(b"a/");
193 map.add_path(b"a/");
225 assert_eq!(3, *map.inner.get(&b"a".to_vec()).unwrap());
194 assert_eq!(3, *map.inner.get(&b"a".to_vec()).unwrap());
226
195
227 // Nested dir adds to its base
196 // Nested dir adds to its base
228 map.add_path(b"a/b/");
197 map.add_path(b"a/b/");
229 assert_eq!(4, *map.inner.get(&b"a".to_vec()).unwrap());
198 assert_eq!(4, *map.inner.get(&b"a".to_vec()).unwrap());
230 assert_eq!(1, *map.inner.get(&b"a/b".to_vec()).unwrap());
199 assert_eq!(1, *map.inner.get(&b"a/b".to_vec()).unwrap());
231
200
232 // but not its base's base, because it already existed
201 // but not its base's base, because it already existed
233 map.add_path(b"a/b/c/");
202 map.add_path(b"a/b/c/");
234 assert_eq!(4, *map.inner.get(&b"a".to_vec()).unwrap());
203 assert_eq!(4, *map.inner.get(&b"a".to_vec()).unwrap());
235 assert_eq!(2, *map.inner.get(&b"a/b".to_vec()).unwrap());
204 assert_eq!(2, *map.inner.get(&b"a/b".to_vec()).unwrap());
236
205
237 map.add_path(b"a/c/");
206 map.add_path(b"a/c/");
238 assert_eq!(1, *map.inner.get(&b"a/c".to_vec()).unwrap());
207 assert_eq!(1, *map.inner.get(&b"a/c".to_vec()).unwrap());
239
208
240 let expected = DirsMultiset {
209 let expected = DirsMultiset {
241 inner: [("", 2), ("a", 5), ("a/b", 2), ("a/b/c", 1), ("a/c", 1)]
210 inner: [("", 2), ("a", 5), ("a/b", 2), ("a/b/c", 1), ("a/c", 1)]
242 .iter()
211 .iter()
243 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
212 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
244 .collect(),
213 .collect(),
245 };
214 };
246 assert_eq!(map, expected);
215 assert_eq!(map, expected);
247 }
216 }
248
217
249 #[test]
218 #[test]
250 fn test_dirsmultiset_new_empty() {
219 fn test_dirsmultiset_new_empty() {
251 use DirsIterable::{Dirstate, Manifest};
220 use DirsIterable::{Dirstate, Manifest};
252
221
253 let new = DirsMultiset::new(Manifest(vec![]), None);
222 let new = DirsMultiset::new(Manifest(vec![]), None);
254 let expected = DirsMultiset {
223 let expected = DirsMultiset {
255 inner: HashMap::new(),
224 inner: HashMap::new(),
256 };
225 };
257 assert_eq!(expected, new);
226 assert_eq!(expected, new);
258
227
259 let new = DirsMultiset::new(Dirstate(vec![]), None);
228 let new = DirsMultiset::new(Dirstate(vec![]), None);
260 let expected = DirsMultiset {
229 let expected = DirsMultiset {
261 inner: HashMap::new(),
230 inner: HashMap::new(),
262 };
231 };
263 assert_eq!(expected, new);
232 assert_eq!(expected, new);
264 }
233 }
265
234
266 #[test]
235 #[test]
267 fn test_dirsmultiset_new_no_skip() {
236 fn test_dirsmultiset_new_no_skip() {
268 use DirsIterable::{Dirstate, Manifest};
237 use DirsIterable::{Dirstate, Manifest};
269
238
270 let input_vec = ["a/", "b/", "a/c", "a/d/"]
239 let input_vec = ["a/", "b/", "a/c", "a/d/"]
271 .iter()
240 .iter()
272 .map(|e| e.as_bytes().to_vec())
241 .map(|e| e.as_bytes().to_vec())
273 .collect();
242 .collect();
274 let expected_inner = [("", 2), ("a", 3), ("b", 1), ("a/d", 1)]
243 let expected_inner = [("", 2), ("a", 3), ("b", 1), ("a/d", 1)]
275 .iter()
244 .iter()
276 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
245 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
277 .collect();
246 .collect();
278
247
279 let new = DirsMultiset::new(Manifest(input_vec), None);
248 let new = DirsMultiset::new(Manifest(input_vec), None);
280 let expected = DirsMultiset {
249 let expected = DirsMultiset {
281 inner: expected_inner,
250 inner: expected_inner,
282 };
251 };
283 assert_eq!(expected, new);
252 assert_eq!(expected, new);
284
253
285 let input_map = ["a/", "b/", "a/c", "a/d/"]
254 let input_map = ["a/", "b/", "a/c", "a/d/"]
286 .iter()
255 .iter()
287 .map(|f| {
256 .map(|f| {
288 (
257 (
289 f.as_bytes().to_vec(),
258 f.as_bytes().to_vec(),
290 DirstateEntry {
259 DirstateEntry {
291 state: 0,
260 state: 0,
292 mode: 0,
261 mode: 0,
293 mtime: 0,
262 mtime: 0,
294 size: 0,
263 size: 0,
295 },
264 },
296 )
265 )
297 })
266 })
298 .collect();
267 .collect();
299 let expected_inner = [("", 2), ("a", 3), ("b", 1), ("a/d", 1)]
268 let expected_inner = [("", 2), ("a", 3), ("b", 1), ("a/d", 1)]
300 .iter()
269 .iter()
301 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
270 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
302 .collect();
271 .collect();
303
272
304 let new = DirsMultiset::new(Dirstate(input_map), None);
273 let new = DirsMultiset::new(Dirstate(input_map), None);
305 let expected = DirsMultiset {
274 let expected = DirsMultiset {
306 inner: expected_inner,
275 inner: expected_inner,
307 };
276 };
308 assert_eq!(expected, new);
277 assert_eq!(expected, new);
309 }
278 }
310
279
311 #[test]
280 #[test]
312 fn test_dirsmultiset_new_skip() {
281 fn test_dirsmultiset_new_skip() {
313 use DirsIterable::{Dirstate, Manifest};
282 use DirsIterable::{Dirstate, Manifest};
314
283
315 let input_vec = ["a/", "b/", "a/c", "a/d/"]
284 let input_vec = ["a/", "b/", "a/c", "a/d/"]
316 .iter()
285 .iter()
317 .map(|e| e.as_bytes().to_vec())
286 .map(|e| e.as_bytes().to_vec())
318 .collect();
287 .collect();
319 let expected_inner = [("", 2), ("a", 3), ("b", 1), ("a/d", 1)]
288 let expected_inner = [("", 2), ("a", 3), ("b", 1), ("a/d", 1)]
320 .iter()
289 .iter()
321 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
290 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
322 .collect();
291 .collect();
323
292
324 let new = DirsMultiset::new(Manifest(input_vec), Some('n' as i8));
293 let new = DirsMultiset::new(Manifest(input_vec), Some('n' as i8));
325 let expected = DirsMultiset {
294 let expected = DirsMultiset {
326 inner: expected_inner,
295 inner: expected_inner,
327 };
296 };
328 // Skip does not affect a manifest
297 // Skip does not affect a manifest
329 assert_eq!(expected, new);
298 assert_eq!(expected, new);
330
299
331 let input_map =
300 let input_map =
332 [("a/", 'n'), ("a/b/", 'n'), ("a/c", 'r'), ("a/d/", 'm')]
301 [("a/", 'n'), ("a/b/", 'n'), ("a/c", 'r'), ("a/d/", 'm')]
333 .iter()
302 .iter()
334 .map(|(f, state)| {
303 .map(|(f, state)| {
335 (
304 (
336 f.as_bytes().to_vec(),
305 f.as_bytes().to_vec(),
337 DirstateEntry {
306 DirstateEntry {
338 state: *state as i8,
307 state: *state as i8,
339 mode: 0,
308 mode: 0,
340 mtime: 0,
309 mtime: 0,
341 size: 0,
310 size: 0,
342 },
311 },
343 )
312 )
344 })
313 })
345 .collect();
314 .collect();
346
315
347 // "a" incremented with "a/c" and "a/d/"
316 // "a" incremented with "a/c" and "a/d/"
348 let expected_inner = [("", 1), ("a", 2), ("a/d", 1)]
317 let expected_inner = [("", 1), ("a", 2), ("a/d", 1)]
349 .iter()
318 .iter()
350 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
319 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
351 .collect();
320 .collect();
352
321
353 let new = DirsMultiset::new(Dirstate(input_map), Some('n' as i8));
322 let new = DirsMultiset::new(Dirstate(input_map), Some('n' as i8));
354 let expected = DirsMultiset {
323 let expected = DirsMultiset {
355 inner: expected_inner,
324 inner: expected_inner,
356 };
325 };
357 assert_eq!(expected, new);
326 assert_eq!(expected, new);
358 }
327 }
359
328
360 }
329 }
General Comments 0
You need to be logged in to leave comments. Login now