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