##// END OF EJS Templates
rust-parsers: switch to parse/pack_dirstate to mutate-on-loop...
Raphaël Gomès -
r42975:c36bdb4e default draft
parent child Browse files
Show More
@@ -1,16 +1,25 b''
1 // dirstate module
2 //
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 //
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.
7
8 use std::collections::HashMap;
9
1 pub mod dirs_multiset;
10 pub mod dirs_multiset;
2 pub mod parsers;
11 pub mod parsers;
3
12
4 #[derive(Debug, PartialEq, Copy, Clone)]
13 #[derive(Debug, PartialEq, Clone)]
5 pub struct DirstateParents<'a> {
14 pub struct DirstateParents {
6 pub p1: &'a [u8],
15 pub p1: [u8; 20],
7 pub p2: &'a [u8],
16 pub p2: [u8; 20],
8 }
17 }
9
18
10 /// The C implementation uses all signed types. This will be an issue
19 /// The C implementation uses all signed types. This will be an issue
11 /// either when 4GB+ source files are commonplace or in 2038, whichever
20 /// either when 4GB+ source files are commonplace or in 2038, whichever
12 /// comes first.
21 /// comes first.
13 #[derive(Debug, PartialEq)]
22 #[derive(Debug, PartialEq, Copy, Clone)]
14 pub struct DirstateEntry {
23 pub struct DirstateEntry {
15 pub state: i8,
24 pub state: i8,
16 pub mode: i32,
25 pub mode: i32,
@@ -18,19 +27,12 b' pub struct DirstateEntry {'
18 pub size: i32,
27 pub size: i32,
19 }
28 }
20
29
21 pub type DirstateVec = Vec<(Vec<u8>, DirstateEntry)>;
30 pub type StateMap = HashMap<Vec<u8>, DirstateEntry>;
22
31 pub type CopyMap = HashMap<Vec<u8>, Vec<u8>>;
23 #[derive(Debug, PartialEq)]
24 pub struct CopyVecEntry<'a> {
25 pub path: &'a [u8],
26 pub copy_path: &'a [u8],
27 }
28
29 pub type CopyVec<'a> = Vec<CopyVecEntry<'a>>;
30
32
31 /// The Python implementation passes either a mapping (dirstate) or a flat
33 /// The Python implementation passes either a mapping (dirstate) or a flat
32 /// iterable (manifest)
34 /// iterable (manifest)
33 pub enum DirsIterable {
35 pub enum DirsIterable<'a> {
34 Dirstate(DirstateVec),
36 Dirstate(&'a HashMap<Vec<u8>, DirstateEntry>),
35 Manifest(Vec<Vec<u8>>),
37 Manifest(&'a Vec<Vec<u8>>),
36 }
38 }
@@ -28,10 +28,10 b' impl DirsMultiset {'
28
28
29 match iterable {
29 match iterable {
30 DirsIterable::Dirstate(vec) => {
30 DirsIterable::Dirstate(vec) => {
31 for (ref filename, DirstateEntry { state, .. }) in vec {
31 for (filename, DirstateEntry { state, .. }) in vec {
32 // This `if` is optimized out of the loop
32 // This `if` is optimized out of the loop
33 if let Some(skip) = skip_state {
33 if let Some(skip) = skip_state {
34 if skip != state {
34 if skip != *state {
35 multiset.add_path(filename);
35 multiset.add_path(filename);
36 }
36 }
37 } else {
37 } else {
@@ -40,7 +40,7 b' impl DirsMultiset {'
40 }
40 }
41 }
41 }
42 DirsIterable::Manifest(vec) => {
42 DirsIterable::Manifest(vec) => {
43 for ref filename in vec {
43 for filename in vec {
44 multiset.add_path(filename);
44 multiset.add_path(filename);
45 }
45 }
46 }
46 }
@@ -108,10 +108,11 b' impl DirsMultiset {'
108 #[cfg(test)]
108 #[cfg(test)]
109 mod tests {
109 mod tests {
110 use super::*;
110 use super::*;
111 use std::collections::HashMap;
111
112
112 #[test]
113 #[test]
113 fn test_delete_path_path_not_found() {
114 fn test_delete_path_path_not_found() {
114 let mut map = DirsMultiset::new(DirsIterable::Manifest(vec![]), None);
115 let mut map = DirsMultiset::new(DirsIterable::Manifest(&vec![]), None);
115 let path = b"doesnotexist/";
116 let path = b"doesnotexist/";
116 assert_eq!(
117 assert_eq!(
117 Err(DirstateMapError::PathNotFound(path.to_vec())),
118 Err(DirstateMapError::PathNotFound(path.to_vec())),
@@ -122,7 +123,7 b' mod tests {'
122 #[test]
123 #[test]
123 fn test_delete_path_empty_path() {
124 fn test_delete_path_empty_path() {
124 let mut map =
125 let mut map =
125 DirsMultiset::new(DirsIterable::Manifest(vec![vec![]]), None);
126 DirsMultiset::new(DirsIterable::Manifest(&vec![vec![]]), None);
126 let path = b"";
127 let path = b"";
127 assert_eq!(Ok(()), map.delete_path(path));
128 assert_eq!(Ok(()), map.delete_path(path));
128 assert_eq!(
129 assert_eq!(
@@ -162,7 +163,7 b' mod tests {'
162
163
163 #[test]
164 #[test]
164 fn test_add_path_empty_path() {
165 fn test_add_path_empty_path() {
165 let mut map = DirsMultiset::new(DirsIterable::Manifest(vec![]), None);
166 let mut map = DirsMultiset::new(DirsIterable::Manifest(&vec![]), None);
166 let path = b"";
167 let path = b"";
167 map.add_path(path);
168 map.add_path(path);
168
169
@@ -171,7 +172,7 b' mod tests {'
171
172
172 #[test]
173 #[test]
173 fn test_add_path_successful() {
174 fn test_add_path_successful() {
174 let mut map = DirsMultiset::new(DirsIterable::Manifest(vec![]), None);
175 let mut map = DirsMultiset::new(DirsIterable::Manifest(&vec![]), None);
175
176
176 map.add_path(b"a/");
177 map.add_path(b"a/");
177 assert_eq!(1, *map.inner.get(&b"a".to_vec()).unwrap());
178 assert_eq!(1, *map.inner.get(&b"a".to_vec()).unwrap());
@@ -218,13 +219,13 b' mod tests {'
218 fn test_dirsmultiset_new_empty() {
219 fn test_dirsmultiset_new_empty() {
219 use DirsIterable::{Dirstate, Manifest};
220 use DirsIterable::{Dirstate, Manifest};
220
221
221 let new = DirsMultiset::new(Manifest(vec![]), None);
222 let new = DirsMultiset::new(Manifest(&vec![]), None);
222 let expected = DirsMultiset {
223 let expected = DirsMultiset {
223 inner: HashMap::new(),
224 inner: HashMap::new(),
224 };
225 };
225 assert_eq!(expected, new);
226 assert_eq!(expected, new);
226
227
227 let new = DirsMultiset::new(Dirstate(vec![]), None);
228 let new = DirsMultiset::new(Dirstate(&HashMap::new()), None);
228 let expected = DirsMultiset {
229 let expected = DirsMultiset {
229 inner: HashMap::new(),
230 inner: HashMap::new(),
230 };
231 };
@@ -244,7 +245,7 b' mod tests {'
244 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
245 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
245 .collect();
246 .collect();
246
247
247 let new = DirsMultiset::new(Manifest(input_vec), None);
248 let new = DirsMultiset::new(Manifest(&input_vec), None);
248 let expected = DirsMultiset {
249 let expected = DirsMultiset {
249 inner: expected_inner,
250 inner: expected_inner,
250 };
251 };
@@ -269,7 +270,7 b' mod tests {'
269 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
270 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
270 .collect();
271 .collect();
271
272
272 let new = DirsMultiset::new(Dirstate(input_map), None);
273 let new = DirsMultiset::new(Dirstate(&input_map), None);
273 let expected = DirsMultiset {
274 let expected = DirsMultiset {
274 inner: expected_inner,
275 inner: expected_inner,
275 };
276 };
@@ -289,7 +290,7 b' mod tests {'
289 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
290 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
290 .collect();
291 .collect();
291
292
292 let new = DirsMultiset::new(Manifest(input_vec), Some('n' as i8));
293 let new = DirsMultiset::new(Manifest(&input_vec), Some('n' as i8));
293 let expected = DirsMultiset {
294 let expected = DirsMultiset {
294 inner: expected_inner,
295 inner: expected_inner,
295 };
296 };
@@ -318,11 +319,10 b' mod tests {'
318 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
319 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
319 .collect();
320 .collect();
320
321
321 let new = DirsMultiset::new(Dirstate(input_map), Some('n' as i8));
322 let new = DirsMultiset::new(Dirstate(&input_map), Some('n' as i8));
322 let expected = DirsMultiset {
323 let expected = DirsMultiset {
323 inner: expected_inner,
324 inner: expected_inner,
324 };
325 };
325 assert_eq!(expected, new);
326 assert_eq!(expected, new);
326 }
327 }
327
328 }
328 }
@@ -4,31 +4,35 b''
4 // GNU General Public License version 2 or any later version.
4 // GNU General Public License version 2 or any later version.
5
5
6 use crate::{
6 use crate::{
7 CopyVec, CopyVecEntry, DirstateEntry, DirstatePackError, DirstateParents,
7 dirstate::{CopyMap, StateMap},
8 DirstateParseError, DirstateVec,
8 utils::copy_into_array,
9 DirstateEntry, DirstatePackError, DirstateParents, DirstateParseError,
9 };
10 };
10 use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
11 use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
11 use std::collections::HashMap;
12 use std::convert::TryInto;
12 use std::io::Cursor;
13 use std::io::Cursor;
14 use std::time::Duration;
13
15
14 /// Parents are stored in the dirstate as byte hashes.
16 /// Parents are stored in the dirstate as byte hashes.
15 const PARENT_SIZE: usize = 20;
17 pub const PARENT_SIZE: usize = 20;
16 /// Dirstate entries have a static part of 8 + 32 + 32 + 32 + 32 bits.
18 /// Dirstate entries have a static part of 8 + 32 + 32 + 32 + 32 bits.
17 const MIN_ENTRY_SIZE: usize = 17;
19 const MIN_ENTRY_SIZE: usize = 17;
18
20
21 // TODO parse/pack: is mutate-on-loop better for performance?
22
19 pub fn parse_dirstate(
23 pub fn parse_dirstate(
24 state_map: &mut StateMap,
25 copy_map: &mut CopyMap,
20 contents: &[u8],
26 contents: &[u8],
21 ) -> Result<(DirstateParents, DirstateVec, CopyVec), DirstateParseError> {
27 ) -> Result<DirstateParents, DirstateParseError> {
22 if contents.len() < PARENT_SIZE * 2 {
28 if contents.len() < PARENT_SIZE * 2 {
23 return Err(DirstateParseError::TooLittleData);
29 return Err(DirstateParseError::TooLittleData);
24 }
30 }
25
31
26 let mut dirstate_vec = vec![];
27 let mut copies = vec![];
28 let mut curr_pos = PARENT_SIZE * 2;
32 let mut curr_pos = PARENT_SIZE * 2;
29 let parents = DirstateParents {
33 let parents = DirstateParents {
30 p1: &contents[..PARENT_SIZE],
34 p1: copy_into_array(&contents[..PARENT_SIZE]),
31 p2: &contents[PARENT_SIZE..curr_pos],
35 p2: copy_into_array(&contents[PARENT_SIZE..curr_pos]),
32 };
36 };
33
37
34 while curr_pos < contents.len() {
38 while curr_pos < contents.len() {
@@ -57,9 +61,9 b' pub fn parse_dirstate('
57 };
61 };
58
62
59 if let Some(copy_path) = copy {
63 if let Some(copy_path) = copy {
60 copies.push(CopyVecEntry { path, copy_path });
64 copy_map.insert(path.to_owned(), copy_path.to_owned());
61 };
65 };
62 dirstate_vec.push((
66 state_map.insert(
63 path.to_owned(),
67 path.to_owned(),
64 DirstateEntry {
68 DirstateEntry {
65 state,
69 state,
@@ -67,28 +71,28 b' pub fn parse_dirstate('
67 size,
71 size,
68 mtime,
72 mtime,
69 },
73 },
70 ));
74 );
71 curr_pos = curr_pos + MIN_ENTRY_SIZE + (path_len);
75 curr_pos = curr_pos + MIN_ENTRY_SIZE + (path_len);
72 }
76 }
73
77
74 Ok((parents, dirstate_vec, copies))
78 Ok(parents)
75 }
79 }
76
80
81 /// `now` is the duration in seconds since the Unix epoch
77 pub fn pack_dirstate(
82 pub fn pack_dirstate(
78 dirstate_vec: &DirstateVec,
83 state_map: &mut StateMap,
79 copymap: &HashMap<Vec<u8>, Vec<u8>>,
84 copy_map: &CopyMap,
80 parents: DirstateParents,
85 parents: DirstateParents,
81 now: i32,
86 now: Duration,
82 ) -> Result<(Vec<u8>, DirstateVec), DirstatePackError> {
87 ) -> Result<Vec<u8>, DirstatePackError> {
83 if parents.p1.len() != PARENT_SIZE || parents.p2.len() != PARENT_SIZE {
88 // TODO move away from i32 before 2038.
84 return Err(DirstatePackError::CorruptedParent);
89 let now: i32 = now.as_secs().try_into().expect("time overflow");
85 }
86
90
87 let expected_size: usize = dirstate_vec
91 let expected_size: usize = state_map
88 .iter()
92 .iter()
89 .map(|(ref filename, _)| {
93 .map(|(filename, _)| {
90 let mut length = MIN_ENTRY_SIZE + filename.len();
94 let mut length = MIN_ENTRY_SIZE + filename.len();
91 if let Some(ref copy) = copymap.get(filename) {
95 if let Some(ref copy) = copy_map.get(filename) {
92 length += copy.len() + 1;
96 length += copy.len() + 1;
93 }
97 }
94 length
98 length
@@ -97,13 +101,13 b' pub fn pack_dirstate('
97 let expected_size = expected_size + PARENT_SIZE * 2;
101 let expected_size = expected_size + PARENT_SIZE * 2;
98
102
99 let mut packed = Vec::with_capacity(expected_size);
103 let mut packed = Vec::with_capacity(expected_size);
100 let mut new_dirstate_vec = vec![];
104 let mut new_state_map = vec![];
101
105
102 packed.extend(parents.p1);
106 packed.extend(&parents.p1);
103 packed.extend(parents.p2);
107 packed.extend(&parents.p2);
104
108
105 for (ref filename, entry) in dirstate_vec {
109 for (ref filename, entry) in state_map.iter() {
106 let mut new_filename: Vec<u8> = filename.to_owned();
110 let mut new_filename: Vec<u8> = filename.to_vec();
107 let mut new_mtime: i32 = entry.mtime;
111 let mut new_mtime: i32 = entry.mtime;
108 if entry.state == 'n' as i8 && entry.mtime == now.into() {
112 if entry.state == 'n' as i8 && entry.mtime == now.into() {
109 // The file was last modified "simultaneously" with the current
113 // The file was last modified "simultaneously" with the current
@@ -116,8 +120,8 b' pub fn pack_dirstate('
116 // contents of the file if the size is the same. This prevents
120 // contents of the file if the size is the same. This prevents
117 // mistakenly treating such files as clean.
121 // mistakenly treating such files as clean.
118 new_mtime = -1;
122 new_mtime = -1;
119 new_dirstate_vec.push((
123 new_state_map.push((
120 filename.to_owned(),
124 filename.to_owned().to_vec(),
121 DirstateEntry {
125 DirstateEntry {
122 mtime: new_mtime,
126 mtime: new_mtime,
123 ..*entry
127 ..*entry
@@ -125,7 +129,7 b' pub fn pack_dirstate('
125 ));
129 ));
126 }
130 }
127
131
128 if let Some(copy) = copymap.get(filename) {
132 if let Some(copy) = copy_map.get(*filename) {
129 new_filename.push('\0' as u8);
133 new_filename.push('\0' as u8);
130 new_filename.extend(copy);
134 new_filename.extend(copy);
131 }
135 }
@@ -142,66 +146,36 b' pub fn pack_dirstate('
142 return Err(DirstatePackError::BadSize(expected_size, packed.len()));
146 return Err(DirstatePackError::BadSize(expected_size, packed.len()));
143 }
147 }
144
148
145 Ok((packed, new_dirstate_vec))
149 state_map.extend(new_state_map);
150
151 Ok(packed)
146 }
152 }
147
148 #[cfg(test)]
153 #[cfg(test)]
149 mod tests {
154 mod tests {
150 use super::*;
155 use super::*;
156 use std::collections::HashMap;
151
157
152 #[test]
158 #[test]
153 fn test_pack_dirstate_empty() {
159 fn test_pack_dirstate_empty() {
154 let dirstate_vec: DirstateVec = vec![];
160 let mut state_map: StateMap = HashMap::new();
155 let copymap = HashMap::new();
161 let copymap = HashMap::new();
156 let parents = DirstateParents {
162 let parents = DirstateParents {
157 p1: b"12345678910111213141",
163 p1: *b"12345678910111213141",
158 p2: b"00000000000000000000",
164 p2: *b"00000000000000000000",
159 };
165 };
160 let now: i32 = 15000000;
166 let now = Duration::new(15000000, 0);
161 let expected =
167 let expected = b"1234567891011121314100000000000000000000".to_vec();
162 (b"1234567891011121314100000000000000000000".to_vec(), vec![]);
163
168
164 assert_eq!(
169 assert_eq!(
165 expected,
170 expected,
166 pack_dirstate(&dirstate_vec, &copymap, parents, now).unwrap()
171 pack_dirstate(&mut state_map, &copymap, parents, now).unwrap()
167 );
172 );
173
174 assert!(state_map.is_empty())
168 }
175 }
169 #[test]
176 #[test]
170 fn test_pack_dirstate_one_entry() {
177 fn test_pack_dirstate_one_entry() {
171 let dirstate_vec: DirstateVec = vec![(
178 let expected_state_map: StateMap = [(
172 vec!['f' as u8, '1' as u8],
173 DirstateEntry {
174 state: 'n' as i8,
175 mode: 0o644,
176 size: 0,
177 mtime: 791231220,
178 },
179 )];
180 let copymap = HashMap::new();
181 let parents = DirstateParents {
182 p1: b"12345678910111213141",
183 p2: b"00000000000000000000",
184 };
185 let now: i32 = 15000000;
186 let expected = (
187 [
188 49, 50, 51, 52, 53, 54, 55, 56, 57, 49, 48, 49, 49, 49, 50,
189 49, 51, 49, 52, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
190 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 110, 0, 0, 1, 164, 0,
191 0, 0, 0, 47, 41, 58, 244, 0, 0, 0, 2, 102, 49,
192 ]
193 .to_vec(),
194 vec![],
195 );
196
197 assert_eq!(
198 expected,
199 pack_dirstate(&dirstate_vec, &copymap, parents, now).unwrap()
200 );
201 }
202 #[test]
203 fn test_pack_dirstate_one_entry_with_copy() {
204 let dirstate_vec: DirstateVec = vec![(
205 b"f1".to_vec(),
179 b"f1".to_vec(),
206 DirstateEntry {
180 DirstateEntry {
207 state: 'n' as i8,
181 state: 'n' as i8,
@@ -209,35 +183,36 b' mod tests {'
209 size: 0,
183 size: 0,
210 mtime: 791231220,
184 mtime: 791231220,
211 },
185 },
212 )];
186 )]
213 let mut copymap = HashMap::new();
187 .iter()
214 copymap.insert(b"f1".to_vec(), b"copyname".to_vec());
188 .cloned()
189 .collect();
190 let mut state_map = expected_state_map.clone();
191
192 let copymap = HashMap::new();
215 let parents = DirstateParents {
193 let parents = DirstateParents {
216 p1: b"12345678910111213141",
194 p1: *b"12345678910111213141",
217 p2: b"00000000000000000000",
195 p2: *b"00000000000000000000",
218 };
196 };
219 let now: i32 = 15000000;
197 let now = Duration::new(15000000, 0);
220 let expected = (
198 let expected = [
221 [
199 49, 50, 51, 52, 53, 54, 55, 56, 57, 49, 48, 49, 49, 49, 50, 49,
222 49, 50, 51, 52, 53, 54, 55, 56, 57, 49, 48, 49, 49, 49, 50,
200 51, 49, 52, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
223 49, 51, 49, 52, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
201 48, 48, 48, 48, 48, 48, 48, 48, 110, 0, 0, 1, 164, 0, 0, 0, 0, 47,
224 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 110, 0, 0, 1, 164, 0,
202 41, 58, 244, 0, 0, 0, 2, 102, 49,
225 0, 0, 0, 47, 41, 58, 244, 0, 0, 0, 11, 102, 49, 0, 99, 111,
203 ]
226 112, 121, 110, 97, 109, 101,
204 .to_vec();
227 ]
228 .to_vec(),
229 vec![],
230 );
231
205
232 assert_eq!(
206 assert_eq!(
233 expected,
207 expected,
234 pack_dirstate(&dirstate_vec, &copymap, parents, now).unwrap()
208 pack_dirstate(&mut state_map, &copymap, parents, now).unwrap()
235 );
209 );
236 }
237
210
211 assert_eq!(expected_state_map, state_map);
212 }
238 #[test]
213 #[test]
239 fn test_parse_pack_one_entry_with_copy() {
214 fn test_pack_dirstate_one_entry_with_copy() {
240 let dirstate_vec: DirstateVec = vec![(
215 let expected_state_map: StateMap = [(
241 b"f1".to_vec(),
216 b"f1".to_vec(),
242 DirstateEntry {
217 DirstateEntry {
243 state: 'n' as i8,
218 state: 'n' as i8,
@@ -245,36 +220,76 b' mod tests {'
245 size: 0,
220 size: 0,
246 mtime: 791231220,
221 mtime: 791231220,
247 },
222 },
248 )];
223 )]
224 .iter()
225 .cloned()
226 .collect();
227 let mut state_map = expected_state_map.clone();
249 let mut copymap = HashMap::new();
228 let mut copymap = HashMap::new();
250 copymap.insert(b"f1".to_vec(), b"copyname".to_vec());
229 copymap.insert(b"f1".to_vec(), b"copyname".to_vec());
251 let parents = DirstateParents {
230 let parents = DirstateParents {
252 p1: b"12345678910111213141",
231 p1: *b"12345678910111213141",
253 p2: b"00000000000000000000",
232 p2: *b"00000000000000000000",
254 };
233 };
255 let now: i32 = 15000000;
234 let now = Duration::new(15000000, 0);
256 let result =
235 let expected = [
257 pack_dirstate(&dirstate_vec, &copymap, parents, now).unwrap();
236 49, 50, 51, 52, 53, 54, 55, 56, 57, 49, 48, 49, 49, 49, 50, 49,
237 51, 49, 52, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
238 48, 48, 48, 48, 48, 48, 48, 48, 110, 0, 0, 1, 164, 0, 0, 0, 0, 47,
239 41, 58, 244, 0, 0, 0, 11, 102, 49, 0, 99, 111, 112, 121, 110, 97,
240 109, 101,
241 ]
242 .to_vec();
258
243
259 assert_eq!(
244 assert_eq!(
260 (
245 expected,
261 parents,
246 pack_dirstate(&mut state_map, &copymap, parents, now).unwrap()
262 dirstate_vec,
247 );
263 copymap
248 assert_eq!(expected_state_map, state_map);
264 .iter()
249 }
265 .map(|(k, v)| CopyVecEntry {
250
266 path: k.as_slice(),
251 #[test]
267 copy_path: v.as_slice()
252 fn test_parse_pack_one_entry_with_copy() {
268 })
253 let mut state_map: StateMap = [(
269 .collect()
254 b"f1".to_vec(),
270 ),
255 DirstateEntry {
271 parse_dirstate(result.0.as_slice()).unwrap()
256 state: 'n' as i8,
257 mode: 0o644,
258 size: 0,
259 mtime: 791231220,
260 },
261 )]
262 .iter()
263 .cloned()
264 .collect();
265 let mut copymap = HashMap::new();
266 copymap.insert(b"f1".to_vec(), b"copyname".to_vec());
267 let parents = DirstateParents {
268 p1: *b"12345678910111213141",
269 p2: *b"00000000000000000000",
270 };
271 let now = Duration::new(15000000, 0);
272 let result =
273 pack_dirstate(&mut state_map, &copymap, parents.clone(), now)
274 .unwrap();
275
276 let mut new_state_map: StateMap = HashMap::new();
277 let mut new_copy_map: CopyMap = HashMap::new();
278 let new_parents = parse_dirstate(
279 &mut new_state_map,
280 &mut new_copy_map,
281 result.as_slice(),
282 )
283 .unwrap();
284 assert_eq!(
285 (parents, state_map, copymap),
286 (new_parents, new_state_map, new_copy_map)
272 )
287 )
273 }
288 }
274
289
275 #[test]
290 #[test]
276 fn test_parse_pack_multiple_entries_with_copy() {
291 fn test_parse_pack_multiple_entries_with_copy() {
277 let dirstate_vec: DirstateVec = vec![
292 let mut state_map: StateMap = [
278 (
293 (
279 b"f1".to_vec(),
294 b"f1".to_vec(),
280 DirstateEntry {
295 DirstateEntry {
@@ -311,39 +326,40 b' mod tests {'
311 mtime: -1,
326 mtime: -1,
312 },
327 },
313 ),
328 ),
314 ];
329 ]
330 .iter()
331 .cloned()
332 .collect();
315 let mut copymap = HashMap::new();
333 let mut copymap = HashMap::new();
316 copymap.insert(b"f1".to_vec(), b"copyname".to_vec());
334 copymap.insert(b"f1".to_vec(), b"copyname".to_vec());
317 copymap.insert(b"f4\xF6".to_vec(), b"copyname2".to_vec());
335 copymap.insert(b"f4\xF6".to_vec(), b"copyname2".to_vec());
318 let parents = DirstateParents {
336 let parents = DirstateParents {
319 p1: b"12345678910111213141",
337 p1: *b"12345678910111213141",
320 p2: b"00000000000000000000",
338 p2: *b"00000000000000000000",
321 };
339 };
322 let now: i32 = 15000000;
340 let now = Duration::new(15000000, 0);
323 let result =
341 let result =
324 pack_dirstate(&dirstate_vec, &copymap, parents, now).unwrap();
342 pack_dirstate(&mut state_map, &copymap, parents.clone(), now)
343 .unwrap();
325
344
345 let mut new_state_map: StateMap = HashMap::new();
346 let mut new_copy_map: CopyMap = HashMap::new();
347 let new_parents = parse_dirstate(
348 &mut new_state_map,
349 &mut new_copy_map,
350 result.as_slice(),
351 )
352 .unwrap();
326 assert_eq!(
353 assert_eq!(
327 (parents, dirstate_vec, copymap),
354 (parents, state_map, copymap),
328 parse_dirstate(result.0.as_slice())
355 (new_parents, new_state_map, new_copy_map)
329 .and_then(|(p, dvec, cvec)| Ok((
330 p,
331 dvec,
332 cvec.iter()
333 .map(|entry| (
334 entry.path.to_vec(),
335 entry.copy_path.to_vec()
336 ))
337 .collect()
338 )))
339 .unwrap()
340 )
356 )
341 }
357 }
342
358
343 #[test]
359 #[test]
344 /// https://www.mercurial-scm.org/repo/hg/rev/af3f26b6bba4
360 /// https://www.mercurial-scm.org/repo/hg/rev/af3f26b6bba4
345 fn test_parse_pack_one_entry_with_copy_and_time_conflict() {
361 fn test_parse_pack_one_entry_with_copy_and_time_conflict() {
346 let dirstate_vec: DirstateVec = vec![(
362 let mut state_map: StateMap = [(
347 b"f1".to_vec(),
363 b"f1".to_vec(),
348 DirstateEntry {
364 DirstateEntry {
349 state: 'n' as i8,
365 state: 'n' as i8,
@@ -351,21 +367,34 b' mod tests {'
351 size: 0,
367 size: 0,
352 mtime: 15000000,
368 mtime: 15000000,
353 },
369 },
354 )];
370 )]
371 .iter()
372 .cloned()
373 .collect();
355 let mut copymap = HashMap::new();
374 let mut copymap = HashMap::new();
356 copymap.insert(b"f1".to_vec(), b"copyname".to_vec());
375 copymap.insert(b"f1".to_vec(), b"copyname".to_vec());
357 let parents = DirstateParents {
376 let parents = DirstateParents {
358 p1: b"12345678910111213141",
377 p1: *b"12345678910111213141",
359 p2: b"00000000000000000000",
378 p2: *b"00000000000000000000",
360 };
379 };
361 let now: i32 = 15000000;
380 let now = Duration::new(15000000, 0);
362 let result =
381 let result =
363 pack_dirstate(&dirstate_vec, &copymap, parents, now).unwrap();
382 pack_dirstate(&mut state_map, &copymap, parents.clone(), now)
383 .unwrap();
384
385 let mut new_state_map: StateMap = HashMap::new();
386 let mut new_copy_map: CopyMap = HashMap::new();
387 let new_parents = parse_dirstate(
388 &mut new_state_map,
389 &mut new_copy_map,
390 result.as_slice(),
391 )
392 .unwrap();
364
393
365 assert_eq!(
394 assert_eq!(
366 (
395 (
367 parents,
396 parents,
368 vec![(
397 [(
369 b"f1".to_vec(),
398 b"f1".to_vec(),
370 DirstateEntry {
399 DirstateEntry {
371 state: 'n' as i8,
400 state: 'n' as i8,
@@ -373,16 +402,13 b' mod tests {'
373 size: 0,
402 size: 0,
374 mtime: -1
403 mtime: -1
375 }
404 }
376 )],
405 )]
377 copymap
406 .iter()
378 .iter()
407 .cloned()
379 .map(|(k, v)| CopyVecEntry {
408 .collect::<StateMap>(),
380 path: k.as_slice(),
409 copymap,
381 copy_path: v.as_slice()
382 })
383 .collect()
384 ),
410 ),
385 parse_dirstate(result.0.as_slice()).unwrap()
411 (new_parents, new_state_map, new_copy_map)
386 )
412 )
387 }
413 }
388 }
414 }
@@ -10,9 +10,8 b' pub mod discovery;'
10 pub mod testing; // unconditionally built, for use from integration tests
10 pub mod testing; // unconditionally built, for use from integration tests
11 pub use dirstate::{
11 pub use dirstate::{
12 dirs_multiset::DirsMultiset,
12 dirs_multiset::DirsMultiset,
13 parsers::{pack_dirstate, parse_dirstate},
13 parsers::{pack_dirstate, parse_dirstate, PARENT_SIZE},
14 CopyVec, CopyVecEntry, DirsIterable, DirstateEntry, DirstateParents,
14 CopyMap, DirsIterable, DirstateEntry, DirstateParents, StateMap,
15 DirstateVec,
16 };
15 };
17 mod filepatterns;
16 mod filepatterns;
18 pub mod utils;
17 pub mod utils;
@@ -60,6 +59,7 b' pub enum DirstateParseError {'
60 TooLittleData,
59 TooLittleData,
61 Overflow,
60 Overflow,
62 CorruptedEntry(String),
61 CorruptedEntry(String),
62 Damaged,
63 }
63 }
64
64
65 #[derive(Debug, PartialEq)]
65 #[derive(Debug, PartialEq)]
@@ -1,5 +1,22 b''
1 pub mod files;
1 pub mod files;
2
2
3 use std::convert::AsMut;
4
5 /// Takes a slice and copies it into an array.
6 ///
7 /// # Panics
8 ///
9 /// Will panic if the slice and target array don't have the same length.
10 pub fn copy_into_array<A, T>(slice: &[T]) -> A
11 where
12 A: Sized + Default + AsMut<[T]>,
13 T: Copy,
14 {
15 let mut a = Default::default();
16 <A as AsMut<[T]>>::as_mut(&mut a).copy_from_slice(slice);
17 a
18 }
19
3 /// Replaces the `from` slice with the `to` slice inside the `buf` slice.
20 /// Replaces the `from` slice with the `to` slice inside the `buf` slice.
4 ///
21 ///
5 /// # Examples
22 /// # Examples
@@ -14,7 +14,7 b' use crate::dirstate::dirs_multiset::Dirs'
14 use cpython::{
14 use cpython::{
15 PyBytes, PyDict, PyErr, PyModule, PyObject, PyResult, PySequence, Python,
15 PyBytes, PyDict, PyErr, PyModule, PyObject, PyResult, PySequence, Python,
16 };
16 };
17 use hg::{DirstateEntry, DirstateVec};
17 use hg::{DirstateEntry, StateMap};
18 use libc::{c_char, c_int};
18 use libc::{c_char, c_int};
19 #[cfg(feature = "python27")]
19 #[cfg(feature = "python27")]
20 use python27_sys::PyCapsule_Import;
20 use python27_sys::PyCapsule_Import;
@@ -54,10 +54,7 b' pub fn decapsule_make_dirstate_tuple('
54 }
54 }
55 }
55 }
56
56
57 pub fn extract_dirstate_vec(
57 pub fn extract_dirstate(py: Python, dmap: &PyDict) -> Result<StateMap, PyErr> {
58 py: Python,
59 dmap: &PyDict,
60 ) -> Result<DirstateVec, PyErr> {
61 dmap.items(py)
58 dmap.items(py)
62 .iter()
59 .iter()
63 .map(|(filename, stats)| {
60 .map(|(filename, stats)| {
@@ -15,7 +15,7 b' use cpython::{'
15 ToPyObject,
15 ToPyObject,
16 };
16 };
17
17
18 use crate::dirstate::extract_dirstate_vec;
18 use crate::dirstate::extract_dirstate;
19 use hg::{DirsIterable, DirsMultiset, DirstateMapError};
19 use hg::{DirsIterable, DirsMultiset, DirstateMapError};
20
20
21 py_class!(pub class Dirs |py| {
21 py_class!(pub class Dirs |py| {
@@ -32,12 +32,10 b' py_class!(pub class Dirs |py| {'
32 if let Some(skip) = skip {
32 if let Some(skip) = skip {
33 skip_state = Some(skip.extract::<PyBytes>(py)?.data(py)[0] as i8);
33 skip_state = Some(skip.extract::<PyBytes>(py)?.data(py)[0] as i8);
34 }
34 }
35 let dirs_map;
35 let inner = if let Ok(map) = map.cast_as::<PyDict>(py) {
36
36 let dirstate = extract_dirstate(py, &map)?;
37 if let Ok(map) = map.cast_as::<PyDict>(py) {
37 DirsMultiset::new(
38 let dirstate_vec = extract_dirstate_vec(py, &map)?;
38 DirsIterable::Dirstate(&dirstate),
39 dirs_map = DirsMultiset::new(
40 DirsIterable::Dirstate(dirstate_vec),
41 skip_state,
39 skip_state,
42 )
40 )
43 } else {
41 } else {
@@ -45,13 +43,13 b' py_class!(pub class Dirs |py| {'
45 .iter(py)?
43 .iter(py)?
46 .map(|o| Ok(o?.extract::<PyBytes>(py)?.data(py).to_owned()))
44 .map(|o| Ok(o?.extract::<PyBytes>(py)?.data(py).to_owned()))
47 .collect();
45 .collect();
48 dirs_map = DirsMultiset::new(
46 DirsMultiset::new(
49 DirsIterable::Manifest(map?),
47 DirsIterable::Manifest(&map?),
50 skip_state,
48 skip_state,
51 )
49 )
52 }
50 };
53
51
54 Self::create_instance(py, RefCell::new(dirs_map))
52 Self::create_instance(py, RefCell::new(inner))
55 }
53 }
56
54
57 def addpath(&self, path: PyObject) -> PyResult<PyObject> {
55 def addpath(&self, path: PyObject) -> PyResult<PyObject> {
@@ -12,17 +12,18 b''
12 //!
12 //!
13 use cpython::{
13 use cpython::{
14 exc, PyBytes, PyDict, PyErr, PyInt, PyModule, PyResult, PyTuple, Python,
14 exc, PyBytes, PyDict, PyErr, PyInt, PyModule, PyResult, PyTuple, Python,
15 PythonObject, ToPyObject,
15 ToPyObject,
16 };
16 };
17 use hg::{
17 use hg::{
18 pack_dirstate, parse_dirstate, CopyVecEntry, DirstateEntry,
18 pack_dirstate, parse_dirstate, utils::copy_into_array, DirstateEntry,
19 DirstatePackError, DirstateParents, DirstateParseError,
19 DirstatePackError, DirstateParents, DirstateParseError, PARENT_SIZE,
20 };
20 };
21 use std::collections::HashMap;
21 use std::collections::HashMap;
22
22
23 use libc::c_char;
23 use libc::c_char;
24
24
25 use crate::dirstate::{decapsule_make_dirstate_tuple, extract_dirstate_vec};
25 use crate::dirstate::{decapsule_make_dirstate_tuple, extract_dirstate};
26 use std::time::Duration;
26
27
27 fn parse_dirstate_wrapper(
28 fn parse_dirstate_wrapper(
28 py: Python,
29 py: Python,
@@ -30,12 +31,15 b' fn parse_dirstate_wrapper('
30 copymap: PyDict,
31 copymap: PyDict,
31 st: PyBytes,
32 st: PyBytes,
32 ) -> PyResult<PyTuple> {
33 ) -> PyResult<PyTuple> {
33 match parse_dirstate(st.data(py)) {
34 let mut dirstate_map = HashMap::new();
34 Ok((parents, dirstate_vec, copies)) => {
35 let mut copies = HashMap::new();
35 for (filename, entry) in dirstate_vec {
36
37 match parse_dirstate(&mut dirstate_map, &mut copies, st.data(py)) {
38 Ok(parents) => {
39 for (filename, entry) in dirstate_map {
36 dmap.set_item(
40 dmap.set_item(
37 py,
41 py,
38 PyBytes::new(py, &filename[..]),
42 PyBytes::new(py, &filename),
39 decapsule_make_dirstate_tuple(py)?(
43 decapsule_make_dirstate_tuple(py)?(
40 entry.state as c_char,
44 entry.state as c_char,
41 entry.mode,
45 entry.mode,
@@ -44,15 +48,17 b' fn parse_dirstate_wrapper('
44 ),
48 ),
45 )?;
49 )?;
46 }
50 }
47 for CopyVecEntry { path, copy_path } in copies {
51 for (path, copy_path) in copies {
48 copymap.set_item(
52 copymap.set_item(
49 py,
53 py,
50 PyBytes::new(py, path),
54 PyBytes::new(py, &path),
51 PyBytes::new(py, copy_path),
55 PyBytes::new(py, &copy_path),
52 )?;
56 )?;
53 }
57 }
54 Ok((PyBytes::new(py, parents.p1), PyBytes::new(py, parents.p2))
58 Ok(
55 .to_py_object(py))
59 (PyBytes::new(py, &parents.p1), PyBytes::new(py, &parents.p2))
60 .to_py_object(py),
61 )
56 }
62 }
57 Err(e) => Err(PyErr::new::<exc::ValueError, _>(
63 Err(e) => Err(PyErr::new::<exc::ValueError, _>(
58 py,
64 py,
@@ -64,6 +70,9 b' fn parse_dirstate_wrapper('
64 "overflow in dirstate".to_string()
70 "overflow in dirstate".to_string()
65 }
71 }
66 DirstateParseError::CorruptedEntry(e) => e,
72 DirstateParseError::CorruptedEntry(e) => e,
73 DirstateParseError::Damaged => {
74 "dirstate appears to be damaged".to_string()
75 }
67 },
76 },
68 )),
77 )),
69 }
78 }
@@ -81,7 +90,7 b' fn pack_dirstate_wrapper('
81 let p2 = pl.get_item(py, 1).extract::<PyBytes>(py)?;
90 let p2 = pl.get_item(py, 1).extract::<PyBytes>(py)?;
82 let p2: &[u8] = p2.data(py);
91 let p2: &[u8] = p2.data(py);
83
92
84 let dirstate_vec = extract_dirstate_vec(py, &dmap)?;
93 let mut dirstate_map = extract_dirstate(py, &dmap)?;
85
94
86 let copies: Result<HashMap<Vec<u8>, Vec<u8>>, PyErr> = copymap
95 let copies: Result<HashMap<Vec<u8>, Vec<u8>>, PyErr> = copymap
87 .items(py)
96 .items(py)
@@ -94,13 +103,23 b' fn pack_dirstate_wrapper('
94 })
103 })
95 .collect();
104 .collect();
96
105
106 if p1.len() != PARENT_SIZE || p2.len() != PARENT_SIZE {
107 return Err(PyErr::new::<exc::ValueError, _>(
108 py,
109 "expected a 20-byte hash".to_string(),
110 ));
111 }
112
97 match pack_dirstate(
113 match pack_dirstate(
98 &dirstate_vec,
114 &mut dirstate_map,
99 &copies?,
115 &copies?,
100 DirstateParents { p1, p2 },
116 DirstateParents {
101 now.as_object().extract::<i32>(py)?,
117 p1: copy_into_array(&p1),
118 p2: copy_into_array(&p2),
119 },
120 Duration::from_secs(now.value(py) as u64),
102 ) {
121 ) {
103 Ok((packed, new_dirstate_vec)) => {
122 Ok(packed) => {
104 for (
123 for (
105 filename,
124 filename,
106 DirstateEntry {
125 DirstateEntry {
@@ -109,7 +128,7 b' fn pack_dirstate_wrapper('
109 size,
128 size,
110 mtime,
129 mtime,
111 },
130 },
112 ) in new_dirstate_vec
131 ) in dirstate_map
113 {
132 {
114 dmap.set_item(
133 dmap.set_item(
115 py,
134 py,
General Comments 0
You need to be logged in to leave comments. Login now