##// END OF EJS Templates
rust-parsers: switch to parse/pack_dirstate to mutate-on-loop...
Raphaël Gomès -
r42993:7cae6bc2 default
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,15 +101,15 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 {
109 // The file was last modified "simultaneously" with the current
113 // The file was last modified "simultaneously" with the current
110 // write to dirstate (i.e. within the same second for file-
114 // write to dirstate (i.e. within the same second for file-
111 // systems with a granularity of 1 sec). This commonly happens
115 // systems with a granularity of 1 sec). This commonly happens
@@ -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,37 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
153
148 #[cfg(test)]
154 #[cfg(test)]
149 mod tests {
155 mod tests {
150 use super::*;
156 use super::*;
157 use std::collections::HashMap;
151
158
152 #[test]
159 #[test]
153 fn test_pack_dirstate_empty() {
160 fn test_pack_dirstate_empty() {
154 let dirstate_vec: DirstateVec = vec![];
161 let mut state_map: StateMap = HashMap::new();
155 let copymap = HashMap::new();
162 let copymap = HashMap::new();
156 let parents = DirstateParents {
163 let parents = DirstateParents {
157 p1: b"12345678910111213141",
164 p1: *b"12345678910111213141",
158 p2: b"00000000000000000000",
165 p2: *b"00000000000000000000",
159 };
166 };
160 let now: i32 = 15000000;
167 let now = Duration::new(15000000, 0);
161 let expected =
168 let expected = b"1234567891011121314100000000000000000000".to_vec();
162 (b"1234567891011121314100000000000000000000".to_vec(), vec![]);
163
169
164 assert_eq!(
170 assert_eq!(
165 expected,
171 expected,
166 pack_dirstate(&dirstate_vec, &copymap, parents, now).unwrap()
172 pack_dirstate(&mut state_map, &copymap, parents, now).unwrap()
167 );
173 );
174
175 assert!(state_map.is_empty())
168 }
176 }
169 #[test]
177 #[test]
170 fn test_pack_dirstate_one_entry() {
178 fn test_pack_dirstate_one_entry() {
171 let dirstate_vec: DirstateVec = vec![(
179 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(),
180 b"f1".to_vec(),
206 DirstateEntry {
181 DirstateEntry {
207 state: 'n' as i8,
182 state: 'n' as i8,
@@ -209,35 +184,36 b' mod tests {'
209 size: 0,
184 size: 0,
210 mtime: 791231220,
185 mtime: 791231220,
211 },
186 },
212 )];
187 )]
213 let mut copymap = HashMap::new();
188 .iter()
214 copymap.insert(b"f1".to_vec(), b"copyname".to_vec());
189 .cloned()
190 .collect();
191 let mut state_map = expected_state_map.clone();
192
193 let copymap = HashMap::new();
215 let parents = DirstateParents {
194 let parents = DirstateParents {
216 p1: b"12345678910111213141",
195 p1: *b"12345678910111213141",
217 p2: b"00000000000000000000",
196 p2: *b"00000000000000000000",
218 };
197 };
219 let now: i32 = 15000000;
198 let now = Duration::new(15000000, 0);
220 let expected = (
199 let expected = [
221 [
200 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,
201 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,
202 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,
203 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,
204 ]
226 112, 121, 110, 97, 109, 101,
205 .to_vec();
227 ]
228 .to_vec(),
229 vec![],
230 );
231
206
232 assert_eq!(
207 assert_eq!(
233 expected,
208 expected,
234 pack_dirstate(&dirstate_vec, &copymap, parents, now).unwrap()
209 pack_dirstate(&mut state_map, &copymap, parents, now).unwrap()
235 );
210 );
236 }
237
211
212 assert_eq!(expected_state_map, state_map);
213 }
238 #[test]
214 #[test]
239 fn test_parse_pack_one_entry_with_copy() {
215 fn test_pack_dirstate_one_entry_with_copy() {
240 let dirstate_vec: DirstateVec = vec![(
216 let expected_state_map: StateMap = [(
241 b"f1".to_vec(),
217 b"f1".to_vec(),
242 DirstateEntry {
218 DirstateEntry {
243 state: 'n' as i8,
219 state: 'n' as i8,
@@ -245,36 +221,76 b' mod tests {'
245 size: 0,
221 size: 0,
246 mtime: 791231220,
222 mtime: 791231220,
247 },
223 },
248 )];
224 )]
225 .iter()
226 .cloned()
227 .collect();
228 let mut state_map = expected_state_map.clone();
249 let mut copymap = HashMap::new();
229 let mut copymap = HashMap::new();
250 copymap.insert(b"f1".to_vec(), b"copyname".to_vec());
230 copymap.insert(b"f1".to_vec(), b"copyname".to_vec());
251 let parents = DirstateParents {
231 let parents = DirstateParents {
252 p1: b"12345678910111213141",
232 p1: *b"12345678910111213141",
253 p2: b"00000000000000000000",
233 p2: *b"00000000000000000000",
254 };
234 };
255 let now: i32 = 15000000;
235 let now = Duration::new(15000000, 0);
256 let result =
236 let expected = [
257 pack_dirstate(&dirstate_vec, &copymap, parents, now).unwrap();
237 49, 50, 51, 52, 53, 54, 55, 56, 57, 49, 48, 49, 49, 49, 50, 49,
238 51, 49, 52, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
239 48, 48, 48, 48, 48, 48, 48, 48, 110, 0, 0, 1, 164, 0, 0, 0, 0, 47,
240 41, 58, 244, 0, 0, 0, 11, 102, 49, 0, 99, 111, 112, 121, 110, 97,
241 109, 101,
242 ]
243 .to_vec();
258
244
259 assert_eq!(
245 assert_eq!(
260 (
246 expected,
261 parents,
247 pack_dirstate(&mut state_map, &copymap, parents, now).unwrap()
262 dirstate_vec,
248 );
263 copymap
249 assert_eq!(expected_state_map, state_map);
264 .iter()
250 }
265 .map(|(k, v)| CopyVecEntry {
251
266 path: k.as_slice(),
252 #[test]
267 copy_path: v.as_slice()
253 fn test_parse_pack_one_entry_with_copy() {
268 })
254 let mut state_map: StateMap = [(
269 .collect()
255 b"f1".to_vec(),
270 ),
256 DirstateEntry {
271 parse_dirstate(result.0.as_slice()).unwrap()
257 state: 'n' as i8,
258 mode: 0o644,
259 size: 0,
260 mtime: 791231220,
261 },
262 )]
263 .iter()
264 .cloned()
265 .collect();
266 let mut copymap = HashMap::new();
267 copymap.insert(b"f1".to_vec(), b"copyname".to_vec());
268 let parents = DirstateParents {
269 p1: *b"12345678910111213141",
270 p2: *b"00000000000000000000",
271 };
272 let now = Duration::new(15000000, 0);
273 let result =
274 pack_dirstate(&mut state_map, &copymap, parents.clone(), now)
275 .unwrap();
276
277 let mut new_state_map: StateMap = HashMap::new();
278 let mut new_copy_map: CopyMap = HashMap::new();
279 let new_parents = parse_dirstate(
280 &mut new_state_map,
281 &mut new_copy_map,
282 result.as_slice(),
283 )
284 .unwrap();
285 assert_eq!(
286 (parents, state_map, copymap),
287 (new_parents, new_state_map, new_copy_map)
272 )
288 )
273 }
289 }
274
290
275 #[test]
291 #[test]
276 fn test_parse_pack_multiple_entries_with_copy() {
292 fn test_parse_pack_multiple_entries_with_copy() {
277 let dirstate_vec: DirstateVec = vec![
293 let mut state_map: StateMap = [
278 (
294 (
279 b"f1".to_vec(),
295 b"f1".to_vec(),
280 DirstateEntry {
296 DirstateEntry {
@@ -311,39 +327,40 b' mod tests {'
311 mtime: -1,
327 mtime: -1,
312 },
328 },
313 ),
329 ),
314 ];
330 ]
331 .iter()
332 .cloned()
333 .collect();
315 let mut copymap = HashMap::new();
334 let mut copymap = HashMap::new();
316 copymap.insert(b"f1".to_vec(), b"copyname".to_vec());
335 copymap.insert(b"f1".to_vec(), b"copyname".to_vec());
317 copymap.insert(b"f4\xF6".to_vec(), b"copyname2".to_vec());
336 copymap.insert(b"f4\xF6".to_vec(), b"copyname2".to_vec());
318 let parents = DirstateParents {
337 let parents = DirstateParents {
319 p1: b"12345678910111213141",
338 p1: *b"12345678910111213141",
320 p2: b"00000000000000000000",
339 p2: *b"00000000000000000000",
321 };
340 };
322 let now: i32 = 15000000;
341 let now = Duration::new(15000000, 0);
323 let result =
342 let result =
324 pack_dirstate(&dirstate_vec, &copymap, parents, now).unwrap();
343 pack_dirstate(&mut state_map, &copymap, parents.clone(), now)
344 .unwrap();
325
345
346 let mut new_state_map: StateMap = HashMap::new();
347 let mut new_copy_map: CopyMap = HashMap::new();
348 let new_parents = parse_dirstate(
349 &mut new_state_map,
350 &mut new_copy_map,
351 result.as_slice(),
352 )
353 .unwrap();
326 assert_eq!(
354 assert_eq!(
327 (parents, dirstate_vec, copymap),
355 (parents, state_map, copymap),
328 parse_dirstate(result.0.as_slice())
356 (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 )
357 )
341 }
358 }
342
359
343 #[test]
360 #[test]
344 /// https://www.mercurial-scm.org/repo/hg/rev/af3f26b6bba4
361 /// https://www.mercurial-scm.org/repo/hg/rev/af3f26b6bba4
345 fn test_parse_pack_one_entry_with_copy_and_time_conflict() {
362 fn test_parse_pack_one_entry_with_copy_and_time_conflict() {
346 let dirstate_vec: DirstateVec = vec![(
363 let mut state_map: StateMap = [(
347 b"f1".to_vec(),
364 b"f1".to_vec(),
348 DirstateEntry {
365 DirstateEntry {
349 state: 'n' as i8,
366 state: 'n' as i8,
@@ -351,21 +368,34 b' mod tests {'
351 size: 0,
368 size: 0,
352 mtime: 15000000,
369 mtime: 15000000,
353 },
370 },
354 )];
371 )]
372 .iter()
373 .cloned()
374 .collect();
355 let mut copymap = HashMap::new();
375 let mut copymap = HashMap::new();
356 copymap.insert(b"f1".to_vec(), b"copyname".to_vec());
376 copymap.insert(b"f1".to_vec(), b"copyname".to_vec());
357 let parents = DirstateParents {
377 let parents = DirstateParents {
358 p1: b"12345678910111213141",
378 p1: *b"12345678910111213141",
359 p2: b"00000000000000000000",
379 p2: *b"00000000000000000000",
360 };
380 };
361 let now: i32 = 15000000;
381 let now = Duration::new(15000000, 0);
362 let result =
382 let result =
363 pack_dirstate(&dirstate_vec, &copymap, parents, now).unwrap();
383 pack_dirstate(&mut state_map, &copymap, parents.clone(), now)
384 .unwrap();
385
386 let mut new_state_map: StateMap = HashMap::new();
387 let mut new_copy_map: CopyMap = HashMap::new();
388 let new_parents = parse_dirstate(
389 &mut new_state_map,
390 &mut new_copy_map,
391 result.as_slice(),
392 )
393 .unwrap();
364
394
365 assert_eq!(
395 assert_eq!(
366 (
396 (
367 parents,
397 parents,
368 vec![(
398 [(
369 b"f1".to_vec(),
399 b"f1".to_vec(),
370 DirstateEntry {
400 DirstateEntry {
371 state: 'n' as i8,
401 state: 'n' as i8,
@@ -373,16 +403,13 b' mod tests {'
373 size: 0,
403 size: 0,
374 mtime: -1
404 mtime: -1
375 }
405 }
376 )],
406 )]
377 copymap
407 .iter()
378 .iter()
408 .cloned()
379 .map(|(k, v)| CopyVecEntry {
409 .collect::<StateMap>(),
380 path: k.as_slice(),
410 copymap,
381 copy_path: v.as_slice()
382 })
383 .collect()
384 ),
411 ),
385 parse_dirstate(result.0.as_slice()).unwrap()
412 (new_parents, new_state_map, new_copy_map)
386 )
413 )
387 }
414 }
388 }
415 }
@@ -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