##// END OF EJS Templates
rust: simply use TryInto to convert slice to array...
Yuya Nishihara -
r43067:53995325 default
parent child Browse files
Show More
@@ -1,426 +1,426
1 // dirstate_map.rs
1 // dirstate_map.rs
2 //
2 //
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 //
4 //
5 // This software may be used and distributed according to the terms of the
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
6 // GNU General Public License version 2 or any later version.
7
7
8 use crate::{
8 use crate::{
9 dirstate::{parsers::PARENT_SIZE, EntryState},
9 dirstate::{parsers::PARENT_SIZE, EntryState},
10 pack_dirstate, parse_dirstate,
10 pack_dirstate, parse_dirstate, CopyMap, DirsIterable, DirsMultiset,
11 utils::copy_into_array,
11 DirstateEntry, DirstateError, DirstateMapError, DirstateParents,
12 CopyMap, DirsIterable, DirsMultiset, DirstateEntry, DirstateError,
12 DirstateParseError, StateMap,
13 DirstateMapError, DirstateParents, DirstateParseError, StateMap,
14 };
13 };
15 use core::borrow::Borrow;
14 use core::borrow::Borrow;
16 use std::collections::{HashMap, HashSet};
15 use std::collections::{HashMap, HashSet};
16 use std::convert::TryInto;
17 use std::iter::FromIterator;
17 use std::iter::FromIterator;
18 use std::ops::Deref;
18 use std::ops::Deref;
19 use std::time::Duration;
19 use std::time::Duration;
20
20
21 pub type FileFoldMap = HashMap<Vec<u8>, Vec<u8>>;
21 pub type FileFoldMap = HashMap<Vec<u8>, Vec<u8>>;
22
22
23 const NULL_ID: [u8; 20] = [0; 20];
23 const NULL_ID: [u8; 20] = [0; 20];
24 const MTIME_UNSET: i32 = -1;
24 const MTIME_UNSET: i32 = -1;
25 const SIZE_DIRTY: i32 = -2;
25 const SIZE_DIRTY: i32 = -2;
26
26
27 #[derive(Default)]
27 #[derive(Default)]
28 pub struct DirstateMap {
28 pub struct DirstateMap {
29 state_map: StateMap,
29 state_map: StateMap,
30 pub copy_map: CopyMap,
30 pub copy_map: CopyMap,
31 file_fold_map: Option<FileFoldMap>,
31 file_fold_map: Option<FileFoldMap>,
32 pub dirs: Option<DirsMultiset>,
32 pub dirs: Option<DirsMultiset>,
33 pub all_dirs: Option<DirsMultiset>,
33 pub all_dirs: Option<DirsMultiset>,
34 non_normal_set: HashSet<Vec<u8>>,
34 non_normal_set: HashSet<Vec<u8>>,
35 other_parent_set: HashSet<Vec<u8>>,
35 other_parent_set: HashSet<Vec<u8>>,
36 parents: Option<DirstateParents>,
36 parents: Option<DirstateParents>,
37 dirty_parents: bool,
37 dirty_parents: bool,
38 }
38 }
39
39
40 /// Should only really be used in python interface code, for clarity
40 /// Should only really be used in python interface code, for clarity
41 impl Deref for DirstateMap {
41 impl Deref for DirstateMap {
42 type Target = StateMap;
42 type Target = StateMap;
43
43
44 fn deref(&self) -> &Self::Target {
44 fn deref(&self) -> &Self::Target {
45 &self.state_map
45 &self.state_map
46 }
46 }
47 }
47 }
48
48
49 impl FromIterator<(Vec<u8>, DirstateEntry)> for DirstateMap {
49 impl FromIterator<(Vec<u8>, DirstateEntry)> for DirstateMap {
50 fn from_iter<I: IntoIterator<Item = (Vec<u8>, DirstateEntry)>>(
50 fn from_iter<I: IntoIterator<Item = (Vec<u8>, DirstateEntry)>>(
51 iter: I,
51 iter: I,
52 ) -> Self {
52 ) -> Self {
53 Self {
53 Self {
54 state_map: iter.into_iter().collect(),
54 state_map: iter.into_iter().collect(),
55 ..Self::default()
55 ..Self::default()
56 }
56 }
57 }
57 }
58 }
58 }
59
59
60 impl DirstateMap {
60 impl DirstateMap {
61 pub fn new() -> Self {
61 pub fn new() -> Self {
62 Self::default()
62 Self::default()
63 }
63 }
64
64
65 pub fn clear(&mut self) {
65 pub fn clear(&mut self) {
66 self.state_map.clear();
66 self.state_map.clear();
67 self.copy_map.clear();
67 self.copy_map.clear();
68 self.file_fold_map = None;
68 self.file_fold_map = None;
69 self.non_normal_set.clear();
69 self.non_normal_set.clear();
70 self.other_parent_set.clear();
70 self.other_parent_set.clear();
71 self.set_parents(DirstateParents {
71 self.set_parents(DirstateParents {
72 p1: NULL_ID,
72 p1: NULL_ID,
73 p2: NULL_ID,
73 p2: NULL_ID,
74 })
74 })
75 }
75 }
76
76
77 /// Add a tracked file to the dirstate
77 /// Add a tracked file to the dirstate
78 pub fn add_file(
78 pub fn add_file(
79 &mut self,
79 &mut self,
80 filename: &[u8],
80 filename: &[u8],
81 old_state: EntryState,
81 old_state: EntryState,
82 entry: DirstateEntry,
82 entry: DirstateEntry,
83 ) {
83 ) {
84 if old_state == EntryState::Unknown || old_state == EntryState::Removed
84 if old_state == EntryState::Unknown || old_state == EntryState::Removed
85 {
85 {
86 if let Some(ref mut dirs) = self.dirs {
86 if let Some(ref mut dirs) = self.dirs {
87 dirs.add_path(filename)
87 dirs.add_path(filename)
88 }
88 }
89 }
89 }
90 if old_state == EntryState::Unknown {
90 if old_state == EntryState::Unknown {
91 if let Some(ref mut all_dirs) = self.all_dirs {
91 if let Some(ref mut all_dirs) = self.all_dirs {
92 all_dirs.add_path(filename)
92 all_dirs.add_path(filename)
93 }
93 }
94 }
94 }
95 self.state_map.insert(filename.to_owned(), entry.to_owned());
95 self.state_map.insert(filename.to_owned(), entry.to_owned());
96
96
97 if entry.state != EntryState::Normal || entry.mtime == MTIME_UNSET {
97 if entry.state != EntryState::Normal || entry.mtime == MTIME_UNSET {
98 self.non_normal_set.insert(filename.to_owned());
98 self.non_normal_set.insert(filename.to_owned());
99 }
99 }
100
100
101 if entry.size == SIZE_DIRTY {
101 if entry.size == SIZE_DIRTY {
102 self.other_parent_set.insert(filename.to_owned());
102 self.other_parent_set.insert(filename.to_owned());
103 }
103 }
104 }
104 }
105
105
106 /// Mark a file as removed in the dirstate.
106 /// Mark a file as removed in the dirstate.
107 ///
107 ///
108 /// The `size` parameter is used to store sentinel values that indicate
108 /// The `size` parameter is used to store sentinel values that indicate
109 /// the file's previous state. In the future, we should refactor this
109 /// the file's previous state. In the future, we should refactor this
110 /// to be more explicit about what that state is.
110 /// to be more explicit about what that state is.
111 pub fn remove_file(
111 pub fn remove_file(
112 &mut self,
112 &mut self,
113 filename: &[u8],
113 filename: &[u8],
114 old_state: EntryState,
114 old_state: EntryState,
115 size: i32,
115 size: i32,
116 ) -> Result<(), DirstateMapError> {
116 ) -> Result<(), DirstateMapError> {
117 if old_state != EntryState::Unknown && old_state != EntryState::Removed
117 if old_state != EntryState::Unknown && old_state != EntryState::Removed
118 {
118 {
119 if let Some(ref mut dirs) = self.dirs {
119 if let Some(ref mut dirs) = self.dirs {
120 dirs.delete_path(filename)?;
120 dirs.delete_path(filename)?;
121 }
121 }
122 }
122 }
123 if old_state == EntryState::Unknown {
123 if old_state == EntryState::Unknown {
124 if let Some(ref mut all_dirs) = self.all_dirs {
124 if let Some(ref mut all_dirs) = self.all_dirs {
125 all_dirs.add_path(filename);
125 all_dirs.add_path(filename);
126 }
126 }
127 }
127 }
128
128
129 if let Some(ref mut file_fold_map) = self.file_fold_map {
129 if let Some(ref mut file_fold_map) = self.file_fold_map {
130 file_fold_map.remove(&filename.to_ascii_uppercase());
130 file_fold_map.remove(&filename.to_ascii_uppercase());
131 }
131 }
132 self.state_map.insert(
132 self.state_map.insert(
133 filename.to_owned(),
133 filename.to_owned(),
134 DirstateEntry {
134 DirstateEntry {
135 state: EntryState::Removed,
135 state: EntryState::Removed,
136 mode: 0,
136 mode: 0,
137 size,
137 size,
138 mtime: 0,
138 mtime: 0,
139 },
139 },
140 );
140 );
141 self.non_normal_set.insert(filename.to_owned());
141 self.non_normal_set.insert(filename.to_owned());
142 Ok(())
142 Ok(())
143 }
143 }
144
144
145 /// Remove a file from the dirstate.
145 /// Remove a file from the dirstate.
146 /// Returns `true` if the file was previously recorded.
146 /// Returns `true` if the file was previously recorded.
147 pub fn drop_file(
147 pub fn drop_file(
148 &mut self,
148 &mut self,
149 filename: &[u8],
149 filename: &[u8],
150 old_state: EntryState,
150 old_state: EntryState,
151 ) -> Result<bool, DirstateMapError> {
151 ) -> Result<bool, DirstateMapError> {
152 let exists = self.state_map.remove(filename).is_some();
152 let exists = self.state_map.remove(filename).is_some();
153
153
154 if exists {
154 if exists {
155 if old_state != EntryState::Removed {
155 if old_state != EntryState::Removed {
156 if let Some(ref mut dirs) = self.dirs {
156 if let Some(ref mut dirs) = self.dirs {
157 dirs.delete_path(filename)?;
157 dirs.delete_path(filename)?;
158 }
158 }
159 }
159 }
160 if let Some(ref mut all_dirs) = self.all_dirs {
160 if let Some(ref mut all_dirs) = self.all_dirs {
161 all_dirs.delete_path(filename)?;
161 all_dirs.delete_path(filename)?;
162 }
162 }
163 }
163 }
164 if let Some(ref mut file_fold_map) = self.file_fold_map {
164 if let Some(ref mut file_fold_map) = self.file_fold_map {
165 file_fold_map.remove(&filename.to_ascii_uppercase());
165 file_fold_map.remove(&filename.to_ascii_uppercase());
166 }
166 }
167 self.non_normal_set.remove(filename);
167 self.non_normal_set.remove(filename);
168
168
169 Ok(exists)
169 Ok(exists)
170 }
170 }
171
171
172 pub fn clear_ambiguous_times(
172 pub fn clear_ambiguous_times(
173 &mut self,
173 &mut self,
174 filenames: Vec<Vec<u8>>,
174 filenames: Vec<Vec<u8>>,
175 now: i32,
175 now: i32,
176 ) {
176 ) {
177 for filename in filenames {
177 for filename in filenames {
178 let mut changed = false;
178 let mut changed = false;
179 self.state_map
179 self.state_map
180 .entry(filename.to_owned())
180 .entry(filename.to_owned())
181 .and_modify(|entry| {
181 .and_modify(|entry| {
182 if entry.state == EntryState::Normal && entry.mtime == now
182 if entry.state == EntryState::Normal && entry.mtime == now
183 {
183 {
184 changed = true;
184 changed = true;
185 *entry = DirstateEntry {
185 *entry = DirstateEntry {
186 mtime: MTIME_UNSET,
186 mtime: MTIME_UNSET,
187 ..*entry
187 ..*entry
188 };
188 };
189 }
189 }
190 });
190 });
191 if changed {
191 if changed {
192 self.non_normal_set.insert(filename.to_owned());
192 self.non_normal_set.insert(filename.to_owned());
193 }
193 }
194 }
194 }
195 }
195 }
196
196
197 pub fn non_normal_other_parent_entries(
197 pub fn non_normal_other_parent_entries(
198 &self,
198 &self,
199 ) -> (HashSet<Vec<u8>>, HashSet<Vec<u8>>) {
199 ) -> (HashSet<Vec<u8>>, HashSet<Vec<u8>>) {
200 let mut non_normal = HashSet::new();
200 let mut non_normal = HashSet::new();
201 let mut other_parent = HashSet::new();
201 let mut other_parent = HashSet::new();
202
202
203 for (
203 for (
204 filename,
204 filename,
205 DirstateEntry {
205 DirstateEntry {
206 state, size, mtime, ..
206 state, size, mtime, ..
207 },
207 },
208 ) in self.state_map.iter()
208 ) in self.state_map.iter()
209 {
209 {
210 if *state != EntryState::Normal || *mtime == MTIME_UNSET {
210 if *state != EntryState::Normal || *mtime == MTIME_UNSET {
211 non_normal.insert(filename.to_owned());
211 non_normal.insert(filename.to_owned());
212 }
212 }
213 if *state == EntryState::Normal && *size == SIZE_DIRTY {
213 if *state == EntryState::Normal && *size == SIZE_DIRTY {
214 other_parent.insert(filename.to_owned());
214 other_parent.insert(filename.to_owned());
215 }
215 }
216 }
216 }
217
217
218 (non_normal, other_parent)
218 (non_normal, other_parent)
219 }
219 }
220
220
221 /// Both of these setters and their uses appear to be the simplest way to
221 /// Both of these setters and their uses appear to be the simplest way to
222 /// emulate a Python lazy property, but it is ugly and unidiomatic.
222 /// emulate a Python lazy property, but it is ugly and unidiomatic.
223 /// TODO One day, rewriting this struct using the typestate might be a
223 /// TODO One day, rewriting this struct using the typestate might be a
224 /// good idea.
224 /// good idea.
225 pub fn set_all_dirs(&mut self) {
225 pub fn set_all_dirs(&mut self) {
226 if self.all_dirs.is_none() {
226 if self.all_dirs.is_none() {
227 self.all_dirs = Some(DirsMultiset::new(
227 self.all_dirs = Some(DirsMultiset::new(
228 DirsIterable::Dirstate(&self.state_map),
228 DirsIterable::Dirstate(&self.state_map),
229 None,
229 None,
230 ));
230 ));
231 }
231 }
232 }
232 }
233
233
234 pub fn set_dirs(&mut self) {
234 pub fn set_dirs(&mut self) {
235 if self.dirs.is_none() {
235 if self.dirs.is_none() {
236 self.dirs = Some(DirsMultiset::new(
236 self.dirs = Some(DirsMultiset::new(
237 DirsIterable::Dirstate(&self.state_map),
237 DirsIterable::Dirstate(&self.state_map),
238 Some(EntryState::Removed),
238 Some(EntryState::Removed),
239 ));
239 ));
240 }
240 }
241 }
241 }
242
242
243 pub fn has_tracked_dir(&mut self, directory: &[u8]) -> bool {
243 pub fn has_tracked_dir(&mut self, directory: &[u8]) -> bool {
244 self.set_dirs();
244 self.set_dirs();
245 self.dirs.as_ref().unwrap().contains(directory)
245 self.dirs.as_ref().unwrap().contains(directory)
246 }
246 }
247
247
248 pub fn has_dir(&mut self, directory: &[u8]) -> bool {
248 pub fn has_dir(&mut self, directory: &[u8]) -> bool {
249 self.set_all_dirs();
249 self.set_all_dirs();
250 self.all_dirs.as_ref().unwrap().contains(directory)
250 self.all_dirs.as_ref().unwrap().contains(directory)
251 }
251 }
252
252
253 pub fn parents(
253 pub fn parents(
254 &mut self,
254 &mut self,
255 file_contents: &[u8],
255 file_contents: &[u8],
256 ) -> Result<DirstateParents, DirstateError> {
256 ) -> Result<DirstateParents, DirstateError> {
257 if let Some(ref parents) = self.parents {
257 if let Some(ref parents) = self.parents {
258 return Ok(parents.clone());
258 return Ok(parents.clone());
259 }
259 }
260 let parents;
260 let parents;
261 if file_contents.len() == PARENT_SIZE * 2 {
261 if file_contents.len() == PARENT_SIZE * 2 {
262 parents = DirstateParents {
262 parents = DirstateParents {
263 p1: copy_into_array(&file_contents[..PARENT_SIZE]),
263 p1: file_contents[..PARENT_SIZE].try_into().unwrap(),
264 p2: copy_into_array(
264 p2: file_contents[PARENT_SIZE..PARENT_SIZE * 2]
265 &file_contents[PARENT_SIZE..PARENT_SIZE * 2],
265 .try_into()
266 ),
266 .unwrap(),
267 };
267 };
268 } else if file_contents.is_empty() {
268 } else if file_contents.is_empty() {
269 parents = DirstateParents {
269 parents = DirstateParents {
270 p1: NULL_ID,
270 p1: NULL_ID,
271 p2: NULL_ID,
271 p2: NULL_ID,
272 };
272 };
273 } else {
273 } else {
274 return Err(DirstateError::Parse(DirstateParseError::Damaged));
274 return Err(DirstateError::Parse(DirstateParseError::Damaged));
275 }
275 }
276
276
277 self.parents = Some(parents.to_owned());
277 self.parents = Some(parents.to_owned());
278 Ok(parents.clone())
278 Ok(parents.clone())
279 }
279 }
280
280
281 pub fn set_parents(&mut self, parents: DirstateParents) {
281 pub fn set_parents(&mut self, parents: DirstateParents) {
282 self.parents = Some(parents.clone());
282 self.parents = Some(parents.clone());
283 self.dirty_parents = true;
283 self.dirty_parents = true;
284 }
284 }
285
285
286 pub fn read(
286 pub fn read(
287 &mut self,
287 &mut self,
288 file_contents: &[u8],
288 file_contents: &[u8],
289 ) -> Result<Option<DirstateParents>, DirstateError> {
289 ) -> Result<Option<DirstateParents>, DirstateError> {
290 if file_contents.is_empty() {
290 if file_contents.is_empty() {
291 return Ok(None);
291 return Ok(None);
292 }
292 }
293
293
294 let parents = parse_dirstate(
294 let parents = parse_dirstate(
295 &mut self.state_map,
295 &mut self.state_map,
296 &mut self.copy_map,
296 &mut self.copy_map,
297 file_contents,
297 file_contents,
298 )?;
298 )?;
299
299
300 if !self.dirty_parents {
300 if !self.dirty_parents {
301 self.set_parents(parents.to_owned());
301 self.set_parents(parents.to_owned());
302 }
302 }
303
303
304 Ok(Some(parents))
304 Ok(Some(parents))
305 }
305 }
306
306
307 pub fn pack(
307 pub fn pack(
308 &mut self,
308 &mut self,
309 parents: DirstateParents,
309 parents: DirstateParents,
310 now: Duration,
310 now: Duration,
311 ) -> Result<Vec<u8>, DirstateError> {
311 ) -> Result<Vec<u8>, DirstateError> {
312 let packed =
312 let packed =
313 pack_dirstate(&mut self.state_map, &self.copy_map, parents, now)?;
313 pack_dirstate(&mut self.state_map, &self.copy_map, parents, now)?;
314
314
315 self.dirty_parents = false;
315 self.dirty_parents = false;
316
316
317 let result = self.non_normal_other_parent_entries();
317 let result = self.non_normal_other_parent_entries();
318 self.non_normal_set = result.0;
318 self.non_normal_set = result.0;
319 self.other_parent_set = result.1;
319 self.other_parent_set = result.1;
320 Ok(packed)
320 Ok(packed)
321 }
321 }
322
322
323 pub fn build_file_fold_map(&mut self) -> FileFoldMap {
323 pub fn build_file_fold_map(&mut self) -> FileFoldMap {
324 if let Some(ref file_fold_map) = self.file_fold_map {
324 if let Some(ref file_fold_map) = self.file_fold_map {
325 return file_fold_map.to_owned();
325 return file_fold_map.to_owned();
326 }
326 }
327 let mut new_file_fold_map = FileFoldMap::new();
327 let mut new_file_fold_map = FileFoldMap::new();
328 for (filename, DirstateEntry { state, .. }) in self.state_map.borrow()
328 for (filename, DirstateEntry { state, .. }) in self.state_map.borrow()
329 {
329 {
330 if *state == EntryState::Removed {
330 if *state == EntryState::Removed {
331 new_file_fold_map.insert(
331 new_file_fold_map.insert(
332 filename.to_ascii_uppercase().to_owned(),
332 filename.to_ascii_uppercase().to_owned(),
333 filename.to_owned(),
333 filename.to_owned(),
334 );
334 );
335 }
335 }
336 }
336 }
337 self.file_fold_map = Some(new_file_fold_map);
337 self.file_fold_map = Some(new_file_fold_map);
338 self.file_fold_map.to_owned().unwrap()
338 self.file_fold_map.to_owned().unwrap()
339 }
339 }
340 }
340 }
341
341
342 #[cfg(test)]
342 #[cfg(test)]
343 mod tests {
343 mod tests {
344 use super::*;
344 use super::*;
345
345
346 #[test]
346 #[test]
347 fn test_dirs_multiset() {
347 fn test_dirs_multiset() {
348 let mut map = DirstateMap::new();
348 let mut map = DirstateMap::new();
349 assert!(map.dirs.is_none());
349 assert!(map.dirs.is_none());
350 assert!(map.all_dirs.is_none());
350 assert!(map.all_dirs.is_none());
351
351
352 assert_eq!(false, map.has_dir(b"nope"));
352 assert_eq!(false, map.has_dir(b"nope"));
353 assert!(map.all_dirs.is_some());
353 assert!(map.all_dirs.is_some());
354 assert!(map.dirs.is_none());
354 assert!(map.dirs.is_none());
355
355
356 assert_eq!(false, map.has_tracked_dir(b"nope"));
356 assert_eq!(false, map.has_tracked_dir(b"nope"));
357 assert!(map.dirs.is_some());
357 assert!(map.dirs.is_some());
358 }
358 }
359
359
360 #[test]
360 #[test]
361 fn test_add_file() {
361 fn test_add_file() {
362 let mut map = DirstateMap::new();
362 let mut map = DirstateMap::new();
363
363
364 assert_eq!(0, map.len());
364 assert_eq!(0, map.len());
365
365
366 map.add_file(
366 map.add_file(
367 b"meh",
367 b"meh",
368 EntryState::Normal,
368 EntryState::Normal,
369 DirstateEntry {
369 DirstateEntry {
370 state: EntryState::Normal,
370 state: EntryState::Normal,
371 mode: 1337,
371 mode: 1337,
372 mtime: 1337,
372 mtime: 1337,
373 size: 1337,
373 size: 1337,
374 },
374 },
375 );
375 );
376
376
377 assert_eq!(1, map.len());
377 assert_eq!(1, map.len());
378 assert_eq!(0, map.non_normal_set.len());
378 assert_eq!(0, map.non_normal_set.len());
379 assert_eq!(0, map.other_parent_set.len());
379 assert_eq!(0, map.other_parent_set.len());
380 }
380 }
381
381
382 #[test]
382 #[test]
383 fn test_non_normal_other_parent_entries() {
383 fn test_non_normal_other_parent_entries() {
384 let map: DirstateMap = [
384 let map: DirstateMap = [
385 (b"f1", (EntryState::Removed, 1337, 1337, 1337)),
385 (b"f1", (EntryState::Removed, 1337, 1337, 1337)),
386 (b"f2", (EntryState::Normal, 1337, 1337, -1)),
386 (b"f2", (EntryState::Normal, 1337, 1337, -1)),
387 (b"f3", (EntryState::Normal, 1337, 1337, 1337)),
387 (b"f3", (EntryState::Normal, 1337, 1337, 1337)),
388 (b"f4", (EntryState::Normal, 1337, -2, 1337)),
388 (b"f4", (EntryState::Normal, 1337, -2, 1337)),
389 (b"f5", (EntryState::Added, 1337, 1337, 1337)),
389 (b"f5", (EntryState::Added, 1337, 1337, 1337)),
390 (b"f6", (EntryState::Added, 1337, 1337, -1)),
390 (b"f6", (EntryState::Added, 1337, 1337, -1)),
391 (b"f7", (EntryState::Merged, 1337, 1337, -1)),
391 (b"f7", (EntryState::Merged, 1337, 1337, -1)),
392 (b"f8", (EntryState::Merged, 1337, 1337, 1337)),
392 (b"f8", (EntryState::Merged, 1337, 1337, 1337)),
393 (b"f9", (EntryState::Merged, 1337, -2, 1337)),
393 (b"f9", (EntryState::Merged, 1337, -2, 1337)),
394 (b"fa", (EntryState::Added, 1337, -2, 1337)),
394 (b"fa", (EntryState::Added, 1337, -2, 1337)),
395 (b"fb", (EntryState::Removed, 1337, -2, 1337)),
395 (b"fb", (EntryState::Removed, 1337, -2, 1337)),
396 ]
396 ]
397 .iter()
397 .iter()
398 .map(|(fname, (state, mode, size, mtime))| {
398 .map(|(fname, (state, mode, size, mtime))| {
399 (
399 (
400 fname.to_vec(),
400 fname.to_vec(),
401 DirstateEntry {
401 DirstateEntry {
402 state: *state,
402 state: *state,
403 mode: *mode,
403 mode: *mode,
404 size: *size,
404 size: *size,
405 mtime: *mtime,
405 mtime: *mtime,
406 },
406 },
407 )
407 )
408 })
408 })
409 .collect();
409 .collect();
410
410
411 let non_normal = [
411 let non_normal = [
412 b"f1", b"f2", b"f5", b"f6", b"f7", b"f8", b"f9", b"fa", b"fb",
412 b"f1", b"f2", b"f5", b"f6", b"f7", b"f8", b"f9", b"fa", b"fb",
413 ]
413 ]
414 .iter()
414 .iter()
415 .map(|x| x.to_vec())
415 .map(|x| x.to_vec())
416 .collect();
416 .collect();
417
417
418 let mut other_parent = HashSet::new();
418 let mut other_parent = HashSet::new();
419 other_parent.insert(b"f4".to_vec());
419 other_parent.insert(b"f4".to_vec());
420
420
421 assert_eq!(
421 assert_eq!(
422 (non_normal, other_parent),
422 (non_normal, other_parent),
423 map.non_normal_other_parent_entries()
423 map.non_normal_other_parent_entries()
424 );
424 );
425 }
425 }
426 }
426 }
@@ -1,415 +1,414
1 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
1 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
2 //
2 //
3 // This software may be used and distributed according to the terms of the
3 // This software may be used and distributed according to the terms of the
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 dirstate::{CopyMap, EntryState, StateMap},
7 dirstate::{CopyMap, EntryState, StateMap},
8 utils::copy_into_array,
9 DirstateEntry, DirstatePackError, DirstateParents, DirstateParseError,
8 DirstateEntry, DirstatePackError, DirstateParents, DirstateParseError,
10 };
9 };
11 use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
10 use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
12 use std::convert::{TryFrom, TryInto};
11 use std::convert::{TryFrom, TryInto};
13 use std::io::Cursor;
12 use std::io::Cursor;
14 use std::time::Duration;
13 use std::time::Duration;
15
14
16 /// Parents are stored in the dirstate as byte hashes.
15 /// Parents are stored in the dirstate as byte hashes.
17 pub const PARENT_SIZE: usize = 20;
16 pub const PARENT_SIZE: usize = 20;
18 /// Dirstate entries have a static part of 8 + 32 + 32 + 32 + 32 bits.
17 /// Dirstate entries have a static part of 8 + 32 + 32 + 32 + 32 bits.
19 const MIN_ENTRY_SIZE: usize = 17;
18 const MIN_ENTRY_SIZE: usize = 17;
20
19
21 // TODO parse/pack: is mutate-on-loop better for performance?
20 // TODO parse/pack: is mutate-on-loop better for performance?
22
21
23 pub fn parse_dirstate(
22 pub fn parse_dirstate(
24 state_map: &mut StateMap,
23 state_map: &mut StateMap,
25 copy_map: &mut CopyMap,
24 copy_map: &mut CopyMap,
26 contents: &[u8],
25 contents: &[u8],
27 ) -> Result<DirstateParents, DirstateParseError> {
26 ) -> Result<DirstateParents, DirstateParseError> {
28 if contents.len() < PARENT_SIZE * 2 {
27 if contents.len() < PARENT_SIZE * 2 {
29 return Err(DirstateParseError::TooLittleData);
28 return Err(DirstateParseError::TooLittleData);
30 }
29 }
31
30
32 let mut curr_pos = PARENT_SIZE * 2;
31 let mut curr_pos = PARENT_SIZE * 2;
33 let parents = DirstateParents {
32 let parents = DirstateParents {
34 p1: copy_into_array(&contents[..PARENT_SIZE]),
33 p1: contents[..PARENT_SIZE].try_into().unwrap(),
35 p2: copy_into_array(&contents[PARENT_SIZE..curr_pos]),
34 p2: contents[PARENT_SIZE..curr_pos].try_into().unwrap(),
36 };
35 };
37
36
38 while curr_pos < contents.len() {
37 while curr_pos < contents.len() {
39 if curr_pos + MIN_ENTRY_SIZE > contents.len() {
38 if curr_pos + MIN_ENTRY_SIZE > contents.len() {
40 return Err(DirstateParseError::Overflow);
39 return Err(DirstateParseError::Overflow);
41 }
40 }
42 let entry_bytes = &contents[curr_pos..];
41 let entry_bytes = &contents[curr_pos..];
43
42
44 let mut cursor = Cursor::new(entry_bytes);
43 let mut cursor = Cursor::new(entry_bytes);
45 let state = EntryState::try_from(cursor.read_u8()?)?;
44 let state = EntryState::try_from(cursor.read_u8()?)?;
46 let mode = cursor.read_i32::<BigEndian>()?;
45 let mode = cursor.read_i32::<BigEndian>()?;
47 let size = cursor.read_i32::<BigEndian>()?;
46 let size = cursor.read_i32::<BigEndian>()?;
48 let mtime = cursor.read_i32::<BigEndian>()?;
47 let mtime = cursor.read_i32::<BigEndian>()?;
49 let path_len = cursor.read_i32::<BigEndian>()? as usize;
48 let path_len = cursor.read_i32::<BigEndian>()? as usize;
50
49
51 if path_len > contents.len() - curr_pos {
50 if path_len > contents.len() - curr_pos {
52 return Err(DirstateParseError::Overflow);
51 return Err(DirstateParseError::Overflow);
53 }
52 }
54
53
55 // Slice instead of allocating a Vec needed for `read_exact`
54 // Slice instead of allocating a Vec needed for `read_exact`
56 let path = &entry_bytes[MIN_ENTRY_SIZE..MIN_ENTRY_SIZE + (path_len)];
55 let path = &entry_bytes[MIN_ENTRY_SIZE..MIN_ENTRY_SIZE + (path_len)];
57
56
58 let (path, copy) = match memchr::memchr(0, path) {
57 let (path, copy) = match memchr::memchr(0, path) {
59 None => (path, None),
58 None => (path, None),
60 Some(i) => (&path[..i], Some(&path[(i + 1)..])),
59 Some(i) => (&path[..i], Some(&path[(i + 1)..])),
61 };
60 };
62
61
63 if let Some(copy_path) = copy {
62 if let Some(copy_path) = copy {
64 copy_map.insert(path.to_owned(), copy_path.to_owned());
63 copy_map.insert(path.to_owned(), copy_path.to_owned());
65 };
64 };
66 state_map.insert(
65 state_map.insert(
67 path.to_owned(),
66 path.to_owned(),
68 DirstateEntry {
67 DirstateEntry {
69 state,
68 state,
70 mode,
69 mode,
71 size,
70 size,
72 mtime,
71 mtime,
73 },
72 },
74 );
73 );
75 curr_pos = curr_pos + MIN_ENTRY_SIZE + (path_len);
74 curr_pos = curr_pos + MIN_ENTRY_SIZE + (path_len);
76 }
75 }
77
76
78 Ok(parents)
77 Ok(parents)
79 }
78 }
80
79
81 /// `now` is the duration in seconds since the Unix epoch
80 /// `now` is the duration in seconds since the Unix epoch
82 pub fn pack_dirstate(
81 pub fn pack_dirstate(
83 state_map: &mut StateMap,
82 state_map: &mut StateMap,
84 copy_map: &CopyMap,
83 copy_map: &CopyMap,
85 parents: DirstateParents,
84 parents: DirstateParents,
86 now: Duration,
85 now: Duration,
87 ) -> Result<Vec<u8>, DirstatePackError> {
86 ) -> Result<Vec<u8>, DirstatePackError> {
88 // TODO move away from i32 before 2038.
87 // TODO move away from i32 before 2038.
89 let now: i32 = now.as_secs().try_into().expect("time overflow");
88 let now: i32 = now.as_secs().try_into().expect("time overflow");
90
89
91 let expected_size: usize = state_map
90 let expected_size: usize = state_map
92 .iter()
91 .iter()
93 .map(|(filename, _)| {
92 .map(|(filename, _)| {
94 let mut length = MIN_ENTRY_SIZE + filename.len();
93 let mut length = MIN_ENTRY_SIZE + filename.len();
95 if let Some(copy) = copy_map.get(filename) {
94 if let Some(copy) = copy_map.get(filename) {
96 length += copy.len() + 1;
95 length += copy.len() + 1;
97 }
96 }
98 length
97 length
99 })
98 })
100 .sum();
99 .sum();
101 let expected_size = expected_size + PARENT_SIZE * 2;
100 let expected_size = expected_size + PARENT_SIZE * 2;
102
101
103 let mut packed = Vec::with_capacity(expected_size);
102 let mut packed = Vec::with_capacity(expected_size);
104 let mut new_state_map = vec![];
103 let mut new_state_map = vec![];
105
104
106 packed.extend(&parents.p1);
105 packed.extend(&parents.p1);
107 packed.extend(&parents.p2);
106 packed.extend(&parents.p2);
108
107
109 for (filename, entry) in state_map.iter() {
108 for (filename, entry) in state_map.iter() {
110 let mut new_filename: Vec<u8> = filename.to_owned();
109 let mut new_filename: Vec<u8> = filename.to_owned();
111 let mut new_mtime: i32 = entry.mtime;
110 let mut new_mtime: i32 = entry.mtime;
112 if entry.state == EntryState::Normal && entry.mtime == now {
111 if entry.state == EntryState::Normal && entry.mtime == now {
113 // The file was last modified "simultaneously" with the current
112 // The file was last modified "simultaneously" with the current
114 // write to dirstate (i.e. within the same second for file-
113 // write to dirstate (i.e. within the same second for file-
115 // systems with a granularity of 1 sec). This commonly happens
114 // systems with a granularity of 1 sec). This commonly happens
116 // for at least a couple of files on 'update'.
115 // for at least a couple of files on 'update'.
117 // The user could change the file without changing its size
116 // The user could change the file without changing its size
118 // within the same second. Invalidate the file's mtime in
117 // within the same second. Invalidate the file's mtime in
119 // dirstate, forcing future 'status' calls to compare the
118 // dirstate, forcing future 'status' calls to compare the
120 // contents of the file if the size is the same. This prevents
119 // contents of the file if the size is the same. This prevents
121 // mistakenly treating such files as clean.
120 // mistakenly treating such files as clean.
122 new_mtime = -1;
121 new_mtime = -1;
123 new_state_map.push((
122 new_state_map.push((
124 filename.to_owned(),
123 filename.to_owned(),
125 DirstateEntry {
124 DirstateEntry {
126 mtime: new_mtime,
125 mtime: new_mtime,
127 ..*entry
126 ..*entry
128 },
127 },
129 ));
128 ));
130 }
129 }
131
130
132 if let Some(copy) = copy_map.get(filename) {
131 if let Some(copy) = copy_map.get(filename) {
133 new_filename.push('\0' as u8);
132 new_filename.push('\0' as u8);
134 new_filename.extend(copy);
133 new_filename.extend(copy);
135 }
134 }
136
135
137 packed.write_u8(entry.state.into())?;
136 packed.write_u8(entry.state.into())?;
138 packed.write_i32::<BigEndian>(entry.mode)?;
137 packed.write_i32::<BigEndian>(entry.mode)?;
139 packed.write_i32::<BigEndian>(entry.size)?;
138 packed.write_i32::<BigEndian>(entry.size)?;
140 packed.write_i32::<BigEndian>(new_mtime)?;
139 packed.write_i32::<BigEndian>(new_mtime)?;
141 packed.write_i32::<BigEndian>(new_filename.len() as i32)?;
140 packed.write_i32::<BigEndian>(new_filename.len() as i32)?;
142 packed.extend(new_filename)
141 packed.extend(new_filename)
143 }
142 }
144
143
145 if packed.len() != expected_size {
144 if packed.len() != expected_size {
146 return Err(DirstatePackError::BadSize(expected_size, packed.len()));
145 return Err(DirstatePackError::BadSize(expected_size, packed.len()));
147 }
146 }
148
147
149 state_map.extend(new_state_map);
148 state_map.extend(new_state_map);
150
149
151 Ok(packed)
150 Ok(packed)
152 }
151 }
153
152
154 #[cfg(test)]
153 #[cfg(test)]
155 mod tests {
154 mod tests {
156 use super::*;
155 use super::*;
157 use std::collections::HashMap;
156 use std::collections::HashMap;
158
157
159 #[test]
158 #[test]
160 fn test_pack_dirstate_empty() {
159 fn test_pack_dirstate_empty() {
161 let mut state_map: StateMap = HashMap::new();
160 let mut state_map: StateMap = HashMap::new();
162 let copymap = HashMap::new();
161 let copymap = HashMap::new();
163 let parents = DirstateParents {
162 let parents = DirstateParents {
164 p1: *b"12345678910111213141",
163 p1: *b"12345678910111213141",
165 p2: *b"00000000000000000000",
164 p2: *b"00000000000000000000",
166 };
165 };
167 let now = Duration::new(15000000, 0);
166 let now = Duration::new(15000000, 0);
168 let expected = b"1234567891011121314100000000000000000000".to_vec();
167 let expected = b"1234567891011121314100000000000000000000".to_vec();
169
168
170 assert_eq!(
169 assert_eq!(
171 expected,
170 expected,
172 pack_dirstate(&mut state_map, &copymap, parents, now).unwrap()
171 pack_dirstate(&mut state_map, &copymap, parents, now).unwrap()
173 );
172 );
174
173
175 assert!(state_map.is_empty())
174 assert!(state_map.is_empty())
176 }
175 }
177 #[test]
176 #[test]
178 fn test_pack_dirstate_one_entry() {
177 fn test_pack_dirstate_one_entry() {
179 let expected_state_map: StateMap = [(
178 let expected_state_map: StateMap = [(
180 b"f1".to_vec(),
179 b"f1".to_vec(),
181 DirstateEntry {
180 DirstateEntry {
182 state: EntryState::Normal,
181 state: EntryState::Normal,
183 mode: 0o644,
182 mode: 0o644,
184 size: 0,
183 size: 0,
185 mtime: 791231220,
184 mtime: 791231220,
186 },
185 },
187 )]
186 )]
188 .iter()
187 .iter()
189 .cloned()
188 .cloned()
190 .collect();
189 .collect();
191 let mut state_map = expected_state_map.clone();
190 let mut state_map = expected_state_map.clone();
192
191
193 let copymap = HashMap::new();
192 let copymap = HashMap::new();
194 let parents = DirstateParents {
193 let parents = DirstateParents {
195 p1: *b"12345678910111213141",
194 p1: *b"12345678910111213141",
196 p2: *b"00000000000000000000",
195 p2: *b"00000000000000000000",
197 };
196 };
198 let now = Duration::new(15000000, 0);
197 let now = Duration::new(15000000, 0);
199 let expected = [
198 let expected = [
200 49, 50, 51, 52, 53, 54, 55, 56, 57, 49, 48, 49, 49, 49, 50, 49,
199 49, 50, 51, 52, 53, 54, 55, 56, 57, 49, 48, 49, 49, 49, 50, 49,
201 51, 49, 52, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
200 51, 49, 52, 49, 48, 48, 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,
201 48, 48, 48, 48, 48, 48, 48, 48, 110, 0, 0, 1, 164, 0, 0, 0, 0, 47,
203 41, 58, 244, 0, 0, 0, 2, 102, 49,
202 41, 58, 244, 0, 0, 0, 2, 102, 49,
204 ]
203 ]
205 .to_vec();
204 .to_vec();
206
205
207 assert_eq!(
206 assert_eq!(
208 expected,
207 expected,
209 pack_dirstate(&mut state_map, &copymap, parents, now).unwrap()
208 pack_dirstate(&mut state_map, &copymap, parents, now).unwrap()
210 );
209 );
211
210
212 assert_eq!(expected_state_map, state_map);
211 assert_eq!(expected_state_map, state_map);
213 }
212 }
214 #[test]
213 #[test]
215 fn test_pack_dirstate_one_entry_with_copy() {
214 fn test_pack_dirstate_one_entry_with_copy() {
216 let expected_state_map: StateMap = [(
215 let expected_state_map: StateMap = [(
217 b"f1".to_vec(),
216 b"f1".to_vec(),
218 DirstateEntry {
217 DirstateEntry {
219 state: EntryState::Normal,
218 state: EntryState::Normal,
220 mode: 0o644,
219 mode: 0o644,
221 size: 0,
220 size: 0,
222 mtime: 791231220,
221 mtime: 791231220,
223 },
222 },
224 )]
223 )]
225 .iter()
224 .iter()
226 .cloned()
225 .cloned()
227 .collect();
226 .collect();
228 let mut state_map = expected_state_map.clone();
227 let mut state_map = expected_state_map.clone();
229 let mut copymap = HashMap::new();
228 let mut copymap = HashMap::new();
230 copymap.insert(b"f1".to_vec(), b"copyname".to_vec());
229 copymap.insert(b"f1".to_vec(), b"copyname".to_vec());
231 let parents = DirstateParents {
230 let parents = DirstateParents {
232 p1: *b"12345678910111213141",
231 p1: *b"12345678910111213141",
233 p2: *b"00000000000000000000",
232 p2: *b"00000000000000000000",
234 };
233 };
235 let now = Duration::new(15000000, 0);
234 let now = Duration::new(15000000, 0);
236 let expected = [
235 let expected = [
237 49, 50, 51, 52, 53, 54, 55, 56, 57, 49, 48, 49, 49, 49, 50, 49,
236 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,
237 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,
238 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,
239 41, 58, 244, 0, 0, 0, 11, 102, 49, 0, 99, 111, 112, 121, 110, 97,
241 109, 101,
240 109, 101,
242 ]
241 ]
243 .to_vec();
242 .to_vec();
244
243
245 assert_eq!(
244 assert_eq!(
246 expected,
245 expected,
247 pack_dirstate(&mut state_map, &copymap, parents, now).unwrap()
246 pack_dirstate(&mut state_map, &copymap, parents, now).unwrap()
248 );
247 );
249 assert_eq!(expected_state_map, state_map);
248 assert_eq!(expected_state_map, state_map);
250 }
249 }
251
250
252 #[test]
251 #[test]
253 fn test_parse_pack_one_entry_with_copy() {
252 fn test_parse_pack_one_entry_with_copy() {
254 let mut state_map: StateMap = [(
253 let mut state_map: StateMap = [(
255 b"f1".to_vec(),
254 b"f1".to_vec(),
256 DirstateEntry {
255 DirstateEntry {
257 state: EntryState::Normal,
256 state: EntryState::Normal,
258 mode: 0o644,
257 mode: 0o644,
259 size: 0,
258 size: 0,
260 mtime: 791231220,
259 mtime: 791231220,
261 },
260 },
262 )]
261 )]
263 .iter()
262 .iter()
264 .cloned()
263 .cloned()
265 .collect();
264 .collect();
266 let mut copymap = HashMap::new();
265 let mut copymap = HashMap::new();
267 copymap.insert(b"f1".to_vec(), b"copyname".to_vec());
266 copymap.insert(b"f1".to_vec(), b"copyname".to_vec());
268 let parents = DirstateParents {
267 let parents = DirstateParents {
269 p1: *b"12345678910111213141",
268 p1: *b"12345678910111213141",
270 p2: *b"00000000000000000000",
269 p2: *b"00000000000000000000",
271 };
270 };
272 let now = Duration::new(15000000, 0);
271 let now = Duration::new(15000000, 0);
273 let result =
272 let result =
274 pack_dirstate(&mut state_map, &copymap, parents.clone(), now)
273 pack_dirstate(&mut state_map, &copymap, parents.clone(), now)
275 .unwrap();
274 .unwrap();
276
275
277 let mut new_state_map: StateMap = HashMap::new();
276 let mut new_state_map: StateMap = HashMap::new();
278 let mut new_copy_map: CopyMap = HashMap::new();
277 let mut new_copy_map: CopyMap = HashMap::new();
279 let new_parents = parse_dirstate(
278 let new_parents = parse_dirstate(
280 &mut new_state_map,
279 &mut new_state_map,
281 &mut new_copy_map,
280 &mut new_copy_map,
282 result.as_slice(),
281 result.as_slice(),
283 )
282 )
284 .unwrap();
283 .unwrap();
285 assert_eq!(
284 assert_eq!(
286 (parents, state_map, copymap),
285 (parents, state_map, copymap),
287 (new_parents, new_state_map, new_copy_map)
286 (new_parents, new_state_map, new_copy_map)
288 )
287 )
289 }
288 }
290
289
291 #[test]
290 #[test]
292 fn test_parse_pack_multiple_entries_with_copy() {
291 fn test_parse_pack_multiple_entries_with_copy() {
293 let mut state_map: StateMap = [
292 let mut state_map: StateMap = [
294 (
293 (
295 b"f1".to_vec(),
294 b"f1".to_vec(),
296 DirstateEntry {
295 DirstateEntry {
297 state: EntryState::Normal,
296 state: EntryState::Normal,
298 mode: 0o644,
297 mode: 0o644,
299 size: 0,
298 size: 0,
300 mtime: 791231220,
299 mtime: 791231220,
301 },
300 },
302 ),
301 ),
303 (
302 (
304 b"f2".to_vec(),
303 b"f2".to_vec(),
305 DirstateEntry {
304 DirstateEntry {
306 state: EntryState::Merged,
305 state: EntryState::Merged,
307 mode: 0o777,
306 mode: 0o777,
308 size: 1000,
307 size: 1000,
309 mtime: 791231220,
308 mtime: 791231220,
310 },
309 },
311 ),
310 ),
312 (
311 (
313 b"f3".to_vec(),
312 b"f3".to_vec(),
314 DirstateEntry {
313 DirstateEntry {
315 state: EntryState::Removed,
314 state: EntryState::Removed,
316 mode: 0o644,
315 mode: 0o644,
317 size: 234553,
316 size: 234553,
318 mtime: 791231220,
317 mtime: 791231220,
319 },
318 },
320 ),
319 ),
321 (
320 (
322 b"f4\xF6".to_vec(),
321 b"f4\xF6".to_vec(),
323 DirstateEntry {
322 DirstateEntry {
324 state: EntryState::Added,
323 state: EntryState::Added,
325 mode: 0o644,
324 mode: 0o644,
326 size: -1,
325 size: -1,
327 mtime: -1,
326 mtime: -1,
328 },
327 },
329 ),
328 ),
330 ]
329 ]
331 .iter()
330 .iter()
332 .cloned()
331 .cloned()
333 .collect();
332 .collect();
334 let mut copymap = HashMap::new();
333 let mut copymap = HashMap::new();
335 copymap.insert(b"f1".to_vec(), b"copyname".to_vec());
334 copymap.insert(b"f1".to_vec(), b"copyname".to_vec());
336 copymap.insert(b"f4\xF6".to_vec(), b"copyname2".to_vec());
335 copymap.insert(b"f4\xF6".to_vec(), b"copyname2".to_vec());
337 let parents = DirstateParents {
336 let parents = DirstateParents {
338 p1: *b"12345678910111213141",
337 p1: *b"12345678910111213141",
339 p2: *b"00000000000000000000",
338 p2: *b"00000000000000000000",
340 };
339 };
341 let now = Duration::new(15000000, 0);
340 let now = Duration::new(15000000, 0);
342 let result =
341 let result =
343 pack_dirstate(&mut state_map, &copymap, parents.clone(), now)
342 pack_dirstate(&mut state_map, &copymap, parents.clone(), now)
344 .unwrap();
343 .unwrap();
345
344
346 let mut new_state_map: StateMap = HashMap::new();
345 let mut new_state_map: StateMap = HashMap::new();
347 let mut new_copy_map: CopyMap = HashMap::new();
346 let mut new_copy_map: CopyMap = HashMap::new();
348 let new_parents = parse_dirstate(
347 let new_parents = parse_dirstate(
349 &mut new_state_map,
348 &mut new_state_map,
350 &mut new_copy_map,
349 &mut new_copy_map,
351 result.as_slice(),
350 result.as_slice(),
352 )
351 )
353 .unwrap();
352 .unwrap();
354 assert_eq!(
353 assert_eq!(
355 (parents, state_map, copymap),
354 (parents, state_map, copymap),
356 (new_parents, new_state_map, new_copy_map)
355 (new_parents, new_state_map, new_copy_map)
357 )
356 )
358 }
357 }
359
358
360 #[test]
359 #[test]
361 /// https://www.mercurial-scm.org/repo/hg/rev/af3f26b6bba4
360 /// https://www.mercurial-scm.org/repo/hg/rev/af3f26b6bba4
362 fn test_parse_pack_one_entry_with_copy_and_time_conflict() {
361 fn test_parse_pack_one_entry_with_copy_and_time_conflict() {
363 let mut state_map: StateMap = [(
362 let mut state_map: StateMap = [(
364 b"f1".to_vec(),
363 b"f1".to_vec(),
365 DirstateEntry {
364 DirstateEntry {
366 state: EntryState::Normal,
365 state: EntryState::Normal,
367 mode: 0o644,
366 mode: 0o644,
368 size: 0,
367 size: 0,
369 mtime: 15000000,
368 mtime: 15000000,
370 },
369 },
371 )]
370 )]
372 .iter()
371 .iter()
373 .cloned()
372 .cloned()
374 .collect();
373 .collect();
375 let mut copymap = HashMap::new();
374 let mut copymap = HashMap::new();
376 copymap.insert(b"f1".to_vec(), b"copyname".to_vec());
375 copymap.insert(b"f1".to_vec(), b"copyname".to_vec());
377 let parents = DirstateParents {
376 let parents = DirstateParents {
378 p1: *b"12345678910111213141",
377 p1: *b"12345678910111213141",
379 p2: *b"00000000000000000000",
378 p2: *b"00000000000000000000",
380 };
379 };
381 let now = Duration::new(15000000, 0);
380 let now = Duration::new(15000000, 0);
382 let result =
381 let result =
383 pack_dirstate(&mut state_map, &copymap, parents.clone(), now)
382 pack_dirstate(&mut state_map, &copymap, parents.clone(), now)
384 .unwrap();
383 .unwrap();
385
384
386 let mut new_state_map: StateMap = HashMap::new();
385 let mut new_state_map: StateMap = HashMap::new();
387 let mut new_copy_map: CopyMap = HashMap::new();
386 let mut new_copy_map: CopyMap = HashMap::new();
388 let new_parents = parse_dirstate(
387 let new_parents = parse_dirstate(
389 &mut new_state_map,
388 &mut new_state_map,
390 &mut new_copy_map,
389 &mut new_copy_map,
391 result.as_slice(),
390 result.as_slice(),
392 )
391 )
393 .unwrap();
392 .unwrap();
394
393
395 assert_eq!(
394 assert_eq!(
396 (
395 (
397 parents,
396 parents,
398 [(
397 [(
399 b"f1".to_vec(),
398 b"f1".to_vec(),
400 DirstateEntry {
399 DirstateEntry {
401 state: EntryState::Normal,
400 state: EntryState::Normal,
402 mode: 0o644,
401 mode: 0o644,
403 size: 0,
402 size: 0,
404 mtime: -1
403 mtime: -1
405 }
404 }
406 )]
405 )]
407 .iter()
406 .iter()
408 .cloned()
407 .cloned()
409 .collect::<StateMap>(),
408 .collect::<StateMap>(),
410 copymap,
409 copymap,
411 ),
410 ),
412 (new_parents, new_state_map, new_copy_map)
411 (new_parents, new_state_map, new_copy_map)
413 )
412 )
414 }
413 }
415 }
414 }
@@ -1,101 +1,84
1 // utils module
1 // utils module
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 //! Contains useful functions, traits, structs, etc. for use in core.
8 //! Contains useful functions, traits, structs, etc. for use in core.
9
9
10 pub mod files;
10 pub mod files;
11
11
12 use std::convert::AsMut;
13
14 /// Takes a slice and copies it into an array.
15 ///
16 /// # Panics
17 ///
18 /// Will panic if the slice and target array don't have the same length.
19 pub fn copy_into_array<A, T>(slice: &[T]) -> A
20 where
21 A: Sized + Default + AsMut<[T]>,
22 T: Copy,
23 {
24 let mut a = Default::default();
25 <A as AsMut<[T]>>::as_mut(&mut a).copy_from_slice(slice);
26 a
27 }
28
29 /// Replaces the `from` slice with the `to` slice inside the `buf` slice.
12 /// Replaces the `from` slice with the `to` slice inside the `buf` slice.
30 ///
13 ///
31 /// # Examples
14 /// # Examples
32 ///
15 ///
33 /// ```
16 /// ```
34 /// use crate::hg::utils::replace_slice;
17 /// use crate::hg::utils::replace_slice;
35 /// let mut line = b"I hate writing tests!".to_vec();
18 /// let mut line = b"I hate writing tests!".to_vec();
36 /// replace_slice(&mut line, b"hate", b"love");
19 /// replace_slice(&mut line, b"hate", b"love");
37 /// assert_eq!(
20 /// assert_eq!(
38 /// line,
21 /// line,
39 /// b"I love writing tests!".to_vec()
22 /// b"I love writing tests!".to_vec()
40 ///);
23 ///);
41 ///
24 ///
42 /// ```
25 /// ```
43 pub fn replace_slice<T>(buf: &mut [T], from: &[T], to: &[T])
26 pub fn replace_slice<T>(buf: &mut [T], from: &[T], to: &[T])
44 where
27 where
45 T: Clone + PartialEq,
28 T: Clone + PartialEq,
46 {
29 {
47 if buf.len() < from.len() || from.len() != to.len() {
30 if buf.len() < from.len() || from.len() != to.len() {
48 return;
31 return;
49 }
32 }
50 for i in 0..=buf.len() - from.len() {
33 for i in 0..=buf.len() - from.len() {
51 if buf[i..].starts_with(from) {
34 if buf[i..].starts_with(from) {
52 buf[i..(i + from.len())].clone_from_slice(to);
35 buf[i..(i + from.len())].clone_from_slice(to);
53 }
36 }
54 }
37 }
55 }
38 }
56
39
57 pub trait SliceExt {
40 pub trait SliceExt {
58 fn trim_end(&self) -> &Self;
41 fn trim_end(&self) -> &Self;
59 fn trim_start(&self) -> &Self;
42 fn trim_start(&self) -> &Self;
60 fn trim(&self) -> &Self;
43 fn trim(&self) -> &Self;
61 }
44 }
62
45
63 fn is_not_whitespace(c: &u8) -> bool {
46 fn is_not_whitespace(c: &u8) -> bool {
64 !(*c as char).is_whitespace()
47 !(*c as char).is_whitespace()
65 }
48 }
66
49
67 impl SliceExt for [u8] {
50 impl SliceExt for [u8] {
68 fn trim_end(&self) -> &[u8] {
51 fn trim_end(&self) -> &[u8] {
69 if let Some(last) = self.iter().rposition(is_not_whitespace) {
52 if let Some(last) = self.iter().rposition(is_not_whitespace) {
70 &self[..last + 1]
53 &self[..last + 1]
71 } else {
54 } else {
72 &[]
55 &[]
73 }
56 }
74 }
57 }
75 fn trim_start(&self) -> &[u8] {
58 fn trim_start(&self) -> &[u8] {
76 if let Some(first) = self.iter().position(is_not_whitespace) {
59 if let Some(first) = self.iter().position(is_not_whitespace) {
77 &self[first..]
60 &self[first..]
78 } else {
61 } else {
79 &[]
62 &[]
80 }
63 }
81 }
64 }
82
65
83 /// ```
66 /// ```
84 /// use hg::utils::SliceExt;
67 /// use hg::utils::SliceExt;
85 /// assert_eq!(
68 /// assert_eq!(
86 /// b" to trim ".trim(),
69 /// b" to trim ".trim(),
87 /// b"to trim"
70 /// b"to trim"
88 /// );
71 /// );
89 /// assert_eq!(
72 /// assert_eq!(
90 /// b"to trim ".trim(),
73 /// b"to trim ".trim(),
91 /// b"to trim"
74 /// b"to trim"
92 /// );
75 /// );
93 /// assert_eq!(
76 /// assert_eq!(
94 /// b" to trim".trim(),
77 /// b" to trim".trim(),
95 /// b"to trim"
78 /// b"to trim"
96 /// );
79 /// );
97 /// ```
80 /// ```
98 fn trim(&self) -> &[u8] {
81 fn trim(&self) -> &[u8] {
99 self.trim_start().trim_end()
82 self.trim_start().trim_end()
100 }
83 }
101 }
84 }
@@ -1,508 +1,510
1 // dirstate_map.rs
1 // dirstate_map.rs
2 //
2 //
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 //
4 //
5 // This software may be used and distributed according to the terms of the
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
6 // GNU General Public License version 2 or any later version.
7
7
8 //! Bindings for the `hg::dirstate::dirstate_map` file provided by the
8 //! Bindings for the `hg::dirstate::dirstate_map` file provided by the
9 //! `hg-core` package.
9 //! `hg-core` package.
10
10
11 use std::cell::RefCell;
11 use std::cell::RefCell;
12 use std::convert::TryInto;
12 use std::convert::TryInto;
13 use std::time::Duration;
13 use std::time::Duration;
14
14
15 use cpython::{
15 use cpython::{
16 exc, ObjectProtocol, PyBool, PyBytes, PyClone, PyDict, PyErr, PyObject,
16 exc, ObjectProtocol, PyBool, PyBytes, PyClone, PyDict, PyErr, PyObject,
17 PyResult, PyTuple, Python, PythonObject, ToPyObject,
17 PyResult, PyTuple, Python, PythonObject, ToPyObject,
18 };
18 };
19 use libc::c_char;
19 use libc::c_char;
20
20
21 use crate::{
21 use crate::{
22 dirstate::copymap::{CopyMap, CopyMapItemsIterator, CopyMapKeysIterator},
22 dirstate::copymap::{CopyMap, CopyMapItemsIterator, CopyMapKeysIterator},
23 dirstate::{decapsule_make_dirstate_tuple, dirs_multiset::Dirs},
23 dirstate::{decapsule_make_dirstate_tuple, dirs_multiset::Dirs},
24 ref_sharing::PySharedState,
24 ref_sharing::PySharedState,
25 };
25 };
26 use hg::{
26 use hg::{
27 utils::copy_into_array, DirsIterable, DirsMultiset, DirstateEntry,
27 DirsIterable, DirsMultiset, DirstateEntry,
28 DirstateMap as RustDirstateMap, DirstateParents, DirstateParseError,
28 DirstateMap as RustDirstateMap, DirstateParents, DirstateParseError,
29 EntryState,
29 EntryState,
30 };
30 };
31
31
32 // TODO
32 // TODO
33 // This object needs to share references to multiple members of its Rust
33 // This object needs to share references to multiple members of its Rust
34 // inner struct, namely `copy_map`, `dirs` and `all_dirs`.
34 // inner struct, namely `copy_map`, `dirs` and `all_dirs`.
35 // Right now `CopyMap` is done, but it needs to have an explicit reference
35 // Right now `CopyMap` is done, but it needs to have an explicit reference
36 // to `RustDirstateMap` which itself needs to have an encapsulation for
36 // to `RustDirstateMap` which itself needs to have an encapsulation for
37 // every method in `CopyMap` (copymapcopy, etc.).
37 // every method in `CopyMap` (copymapcopy, etc.).
38 // This is ugly and hard to maintain.
38 // This is ugly and hard to maintain.
39 // The same logic applies to `dirs` and `all_dirs`, however the `Dirs`
39 // The same logic applies to `dirs` and `all_dirs`, however the `Dirs`
40 // `py_class!` is already implemented and does not mention
40 // `py_class!` is already implemented and does not mention
41 // `RustDirstateMap`, rightfully so.
41 // `RustDirstateMap`, rightfully so.
42 // All attributes also have to have a separate refcount data attribute for
42 // All attributes also have to have a separate refcount data attribute for
43 // leaks, with all methods that go along for reference sharing.
43 // leaks, with all methods that go along for reference sharing.
44 py_class!(pub class DirstateMap |py| {
44 py_class!(pub class DirstateMap |py| {
45 data inner: RefCell<RustDirstateMap>;
45 data inner: RefCell<RustDirstateMap>;
46 data py_shared_state: PySharedState;
46 data py_shared_state: PySharedState;
47
47
48 def __new__(_cls, _root: PyObject) -> PyResult<Self> {
48 def __new__(_cls, _root: PyObject) -> PyResult<Self> {
49 let inner = RustDirstateMap::default();
49 let inner = RustDirstateMap::default();
50 Self::create_instance(
50 Self::create_instance(
51 py,
51 py,
52 RefCell::new(inner),
52 RefCell::new(inner),
53 PySharedState::default()
53 PySharedState::default()
54 )
54 )
55 }
55 }
56
56
57 def clear(&self) -> PyResult<PyObject> {
57 def clear(&self) -> PyResult<PyObject> {
58 self.borrow_mut(py)?.clear();
58 self.borrow_mut(py)?.clear();
59 Ok(py.None())
59 Ok(py.None())
60 }
60 }
61
61
62 def get(
62 def get(
63 &self,
63 &self,
64 key: PyObject,
64 key: PyObject,
65 default: Option<PyObject> = None
65 default: Option<PyObject> = None
66 ) -> PyResult<Option<PyObject>> {
66 ) -> PyResult<Option<PyObject>> {
67 let key = key.extract::<PyBytes>(py)?;
67 let key = key.extract::<PyBytes>(py)?;
68 match self.inner(py).borrow().get(key.data(py)) {
68 match self.inner(py).borrow().get(key.data(py)) {
69 Some(entry) => {
69 Some(entry) => {
70 // Explicitly go through u8 first, then cast to
70 // Explicitly go through u8 first, then cast to
71 // platform-specific `c_char`.
71 // platform-specific `c_char`.
72 let state: u8 = entry.state.into();
72 let state: u8 = entry.state.into();
73 Ok(Some(decapsule_make_dirstate_tuple(py)?(
73 Ok(Some(decapsule_make_dirstate_tuple(py)?(
74 state as c_char,
74 state as c_char,
75 entry.mode,
75 entry.mode,
76 entry.size,
76 entry.size,
77 entry.mtime,
77 entry.mtime,
78 )))
78 )))
79 },
79 },
80 None => Ok(default)
80 None => Ok(default)
81 }
81 }
82 }
82 }
83
83
84 def addfile(
84 def addfile(
85 &self,
85 &self,
86 f: PyObject,
86 f: PyObject,
87 oldstate: PyObject,
87 oldstate: PyObject,
88 state: PyObject,
88 state: PyObject,
89 mode: PyObject,
89 mode: PyObject,
90 size: PyObject,
90 size: PyObject,
91 mtime: PyObject
91 mtime: PyObject
92 ) -> PyResult<PyObject> {
92 ) -> PyResult<PyObject> {
93 self.borrow_mut(py)?.add_file(
93 self.borrow_mut(py)?.add_file(
94 f.extract::<PyBytes>(py)?.data(py),
94 f.extract::<PyBytes>(py)?.data(py),
95 oldstate.extract::<PyBytes>(py)?.data(py)[0]
95 oldstate.extract::<PyBytes>(py)?.data(py)[0]
96 .try_into()
96 .try_into()
97 .map_err(|e: DirstateParseError| {
97 .map_err(|e: DirstateParseError| {
98 PyErr::new::<exc::ValueError, _>(py, e.to_string())
98 PyErr::new::<exc::ValueError, _>(py, e.to_string())
99 })?,
99 })?,
100 DirstateEntry {
100 DirstateEntry {
101 state: state.extract::<PyBytes>(py)?.data(py)[0]
101 state: state.extract::<PyBytes>(py)?.data(py)[0]
102 .try_into()
102 .try_into()
103 .map_err(|e: DirstateParseError| {
103 .map_err(|e: DirstateParseError| {
104 PyErr::new::<exc::ValueError, _>(py, e.to_string())
104 PyErr::new::<exc::ValueError, _>(py, e.to_string())
105 })?,
105 })?,
106 mode: mode.extract(py)?,
106 mode: mode.extract(py)?,
107 size: size.extract(py)?,
107 size: size.extract(py)?,
108 mtime: mtime.extract(py)?,
108 mtime: mtime.extract(py)?,
109 },
109 },
110 );
110 );
111 Ok(py.None())
111 Ok(py.None())
112 }
112 }
113
113
114 def removefile(
114 def removefile(
115 &self,
115 &self,
116 f: PyObject,
116 f: PyObject,
117 oldstate: PyObject,
117 oldstate: PyObject,
118 size: PyObject
118 size: PyObject
119 ) -> PyResult<PyObject> {
119 ) -> PyResult<PyObject> {
120 self.borrow_mut(py)?
120 self.borrow_mut(py)?
121 .remove_file(
121 .remove_file(
122 f.extract::<PyBytes>(py)?.data(py),
122 f.extract::<PyBytes>(py)?.data(py),
123 oldstate.extract::<PyBytes>(py)?.data(py)[0]
123 oldstate.extract::<PyBytes>(py)?.data(py)[0]
124 .try_into()
124 .try_into()
125 .map_err(|e: DirstateParseError| {
125 .map_err(|e: DirstateParseError| {
126 PyErr::new::<exc::ValueError, _>(py, e.to_string())
126 PyErr::new::<exc::ValueError, _>(py, e.to_string())
127 })?,
127 })?,
128 size.extract(py)?,
128 size.extract(py)?,
129 )
129 )
130 .or_else(|_| {
130 .or_else(|_| {
131 Err(PyErr::new::<exc::OSError, _>(
131 Err(PyErr::new::<exc::OSError, _>(
132 py,
132 py,
133 "Dirstate error".to_string(),
133 "Dirstate error".to_string(),
134 ))
134 ))
135 })?;
135 })?;
136 Ok(py.None())
136 Ok(py.None())
137 }
137 }
138
138
139 def dropfile(
139 def dropfile(
140 &self,
140 &self,
141 f: PyObject,
141 f: PyObject,
142 oldstate: PyObject
142 oldstate: PyObject
143 ) -> PyResult<PyBool> {
143 ) -> PyResult<PyBool> {
144 self.borrow_mut(py)?
144 self.borrow_mut(py)?
145 .drop_file(
145 .drop_file(
146 f.extract::<PyBytes>(py)?.data(py),
146 f.extract::<PyBytes>(py)?.data(py),
147 oldstate.extract::<PyBytes>(py)?.data(py)[0]
147 oldstate.extract::<PyBytes>(py)?.data(py)[0]
148 .try_into()
148 .try_into()
149 .map_err(|e: DirstateParseError| {
149 .map_err(|e: DirstateParseError| {
150 PyErr::new::<exc::ValueError, _>(py, e.to_string())
150 PyErr::new::<exc::ValueError, _>(py, e.to_string())
151 })?,
151 })?,
152 )
152 )
153 .and_then(|b| Ok(b.to_py_object(py)))
153 .and_then(|b| Ok(b.to_py_object(py)))
154 .or_else(|_| {
154 .or_else(|_| {
155 Err(PyErr::new::<exc::OSError, _>(
155 Err(PyErr::new::<exc::OSError, _>(
156 py,
156 py,
157 "Dirstate error".to_string(),
157 "Dirstate error".to_string(),
158 ))
158 ))
159 })
159 })
160 }
160 }
161
161
162 def clearambiguoustimes(
162 def clearambiguoustimes(
163 &self,
163 &self,
164 files: PyObject,
164 files: PyObject,
165 now: PyObject
165 now: PyObject
166 ) -> PyResult<PyObject> {
166 ) -> PyResult<PyObject> {
167 let files: PyResult<Vec<Vec<u8>>> = files
167 let files: PyResult<Vec<Vec<u8>>> = files
168 .iter(py)?
168 .iter(py)?
169 .map(|filename| {
169 .map(|filename| {
170 Ok(filename?.extract::<PyBytes>(py)?.data(py).to_owned())
170 Ok(filename?.extract::<PyBytes>(py)?.data(py).to_owned())
171 })
171 })
172 .collect();
172 .collect();
173 self.inner(py)
173 self.inner(py)
174 .borrow_mut()
174 .borrow_mut()
175 .clear_ambiguous_times(files?, now.extract(py)?);
175 .clear_ambiguous_times(files?, now.extract(py)?);
176 Ok(py.None())
176 Ok(py.None())
177 }
177 }
178
178
179 // TODO share the reference
179 // TODO share the reference
180 def nonnormalentries(&self) -> PyResult<PyObject> {
180 def nonnormalentries(&self) -> PyResult<PyObject> {
181 let (non_normal, other_parent) =
181 let (non_normal, other_parent) =
182 self.inner(py).borrow().non_normal_other_parent_entries();
182 self.inner(py).borrow().non_normal_other_parent_entries();
183
183
184 let locals = PyDict::new(py);
184 let locals = PyDict::new(py);
185 locals.set_item(
185 locals.set_item(
186 py,
186 py,
187 "non_normal",
187 "non_normal",
188 non_normal
188 non_normal
189 .iter()
189 .iter()
190 .map(|v| PyBytes::new(py, &v))
190 .map(|v| PyBytes::new(py, &v))
191 .collect::<Vec<PyBytes>>()
191 .collect::<Vec<PyBytes>>()
192 .to_py_object(py),
192 .to_py_object(py),
193 )?;
193 )?;
194 locals.set_item(
194 locals.set_item(
195 py,
195 py,
196 "other_parent",
196 "other_parent",
197 other_parent
197 other_parent
198 .iter()
198 .iter()
199 .map(|v| PyBytes::new(py, &v))
199 .map(|v| PyBytes::new(py, &v))
200 .collect::<Vec<PyBytes>>()
200 .collect::<Vec<PyBytes>>()
201 .to_py_object(py),
201 .to_py_object(py),
202 )?;
202 )?;
203
203
204 py.eval("set(non_normal), set(other_parent)", None, Some(&locals))
204 py.eval("set(non_normal), set(other_parent)", None, Some(&locals))
205 }
205 }
206
206
207 def hastrackeddir(&self, d: PyObject) -> PyResult<PyBool> {
207 def hastrackeddir(&self, d: PyObject) -> PyResult<PyBool> {
208 let d = d.extract::<PyBytes>(py)?;
208 let d = d.extract::<PyBytes>(py)?;
209 Ok(self
209 Ok(self
210 .inner(py)
210 .inner(py)
211 .borrow_mut()
211 .borrow_mut()
212 .has_tracked_dir(d.data(py))
212 .has_tracked_dir(d.data(py))
213 .to_py_object(py))
213 .to_py_object(py))
214 }
214 }
215
215
216 def hasdir(&self, d: PyObject) -> PyResult<PyBool> {
216 def hasdir(&self, d: PyObject) -> PyResult<PyBool> {
217 let d = d.extract::<PyBytes>(py)?;
217 let d = d.extract::<PyBytes>(py)?;
218 Ok(self
218 Ok(self
219 .inner(py)
219 .inner(py)
220 .borrow_mut()
220 .borrow_mut()
221 .has_dir(d.data(py))
221 .has_dir(d.data(py))
222 .to_py_object(py))
222 .to_py_object(py))
223 }
223 }
224
224
225 def parents(&self, st: PyObject) -> PyResult<PyTuple> {
225 def parents(&self, st: PyObject) -> PyResult<PyTuple> {
226 self.inner(py)
226 self.inner(py)
227 .borrow_mut()
227 .borrow_mut()
228 .parents(st.extract::<PyBytes>(py)?.data(py))
228 .parents(st.extract::<PyBytes>(py)?.data(py))
229 .and_then(|d| {
229 .and_then(|d| {
230 Ok((PyBytes::new(py, &d.p1), PyBytes::new(py, &d.p2))
230 Ok((PyBytes::new(py, &d.p1), PyBytes::new(py, &d.p2))
231 .to_py_object(py))
231 .to_py_object(py))
232 })
232 })
233 .or_else(|_| {
233 .or_else(|_| {
234 Err(PyErr::new::<exc::OSError, _>(
234 Err(PyErr::new::<exc::OSError, _>(
235 py,
235 py,
236 "Dirstate error".to_string(),
236 "Dirstate error".to_string(),
237 ))
237 ))
238 })
238 })
239 }
239 }
240
240
241 def setparents(&self, p1: PyObject, p2: PyObject) -> PyResult<PyObject> {
241 def setparents(&self, p1: PyObject, p2: PyObject) -> PyResult<PyObject> {
242 let p1 = copy_into_array(p1.extract::<PyBytes>(py)?.data(py));
242 // TODO: don't panic; raise Python exception instead.
243 let p2 = copy_into_array(p2.extract::<PyBytes>(py)?.data(py));
243 let p1 = p1.extract::<PyBytes>(py)?.data(py).try_into().unwrap();
244 let p2 = p2.extract::<PyBytes>(py)?.data(py).try_into().unwrap();
244
245
245 self.inner(py)
246 self.inner(py)
246 .borrow_mut()
247 .borrow_mut()
247 .set_parents(DirstateParents { p1, p2 });
248 .set_parents(DirstateParents { p1, p2 });
248 Ok(py.None())
249 Ok(py.None())
249 }
250 }
250
251
251 def read(&self, st: PyObject) -> PyResult<Option<PyObject>> {
252 def read(&self, st: PyObject) -> PyResult<Option<PyObject>> {
252 match self
253 match self
253 .inner(py)
254 .inner(py)
254 .borrow_mut()
255 .borrow_mut()
255 .read(st.extract::<PyBytes>(py)?.data(py))
256 .read(st.extract::<PyBytes>(py)?.data(py))
256 {
257 {
257 Ok(Some(parents)) => Ok(Some(
258 Ok(Some(parents)) => Ok(Some(
258 (PyBytes::new(py, &parents.p1), PyBytes::new(py, &parents.p2))
259 (PyBytes::new(py, &parents.p1), PyBytes::new(py, &parents.p2))
259 .to_py_object(py)
260 .to_py_object(py)
260 .into_object(),
261 .into_object(),
261 )),
262 )),
262 Ok(None) => Ok(Some(py.None())),
263 Ok(None) => Ok(Some(py.None())),
263 Err(_) => Err(PyErr::new::<exc::OSError, _>(
264 Err(_) => Err(PyErr::new::<exc::OSError, _>(
264 py,
265 py,
265 "Dirstate error".to_string(),
266 "Dirstate error".to_string(),
266 )),
267 )),
267 }
268 }
268 }
269 }
269 def write(
270 def write(
270 &self,
271 &self,
271 p1: PyObject,
272 p1: PyObject,
272 p2: PyObject,
273 p2: PyObject,
273 now: PyObject
274 now: PyObject
274 ) -> PyResult<PyBytes> {
275 ) -> PyResult<PyBytes> {
275 let now = Duration::new(now.extract(py)?, 0);
276 let now = Duration::new(now.extract(py)?, 0);
276 let parents = DirstateParents {
277 let parents = DirstateParents {
277 p1: copy_into_array(p1.extract::<PyBytes>(py)?.data(py)),
278 // TODO: don't panic; raise Python exception instead.
278 p2: copy_into_array(p2.extract::<PyBytes>(py)?.data(py)),
279 p1: p1.extract::<PyBytes>(py)?.data(py).try_into().unwrap(),
280 p2: p2.extract::<PyBytes>(py)?.data(py).try_into().unwrap(),
279 };
281 };
280
282
281 match self.borrow_mut(py)?.pack(parents, now) {
283 match self.borrow_mut(py)?.pack(parents, now) {
282 Ok(packed) => Ok(PyBytes::new(py, &packed)),
284 Ok(packed) => Ok(PyBytes::new(py, &packed)),
283 Err(_) => Err(PyErr::new::<exc::OSError, _>(
285 Err(_) => Err(PyErr::new::<exc::OSError, _>(
284 py,
286 py,
285 "Dirstate error".to_string(),
287 "Dirstate error".to_string(),
286 )),
288 )),
287 }
289 }
288 }
290 }
289
291
290 def filefoldmapasdict(&self) -> PyResult<PyDict> {
292 def filefoldmapasdict(&self) -> PyResult<PyDict> {
291 let dict = PyDict::new(py);
293 let dict = PyDict::new(py);
292 for (key, value) in
294 for (key, value) in
293 self.borrow_mut(py)?.build_file_fold_map().iter()
295 self.borrow_mut(py)?.build_file_fold_map().iter()
294 {
296 {
295 dict.set_item(py, key, value)?;
297 dict.set_item(py, key, value)?;
296 }
298 }
297 Ok(dict)
299 Ok(dict)
298 }
300 }
299
301
300 def __len__(&self) -> PyResult<usize> {
302 def __len__(&self) -> PyResult<usize> {
301 Ok(self.inner(py).borrow().len())
303 Ok(self.inner(py).borrow().len())
302 }
304 }
303
305
304 def __contains__(&self, key: PyObject) -> PyResult<bool> {
306 def __contains__(&self, key: PyObject) -> PyResult<bool> {
305 let key = key.extract::<PyBytes>(py)?;
307 let key = key.extract::<PyBytes>(py)?;
306 Ok(self.inner(py).borrow().contains_key(key.data(py)))
308 Ok(self.inner(py).borrow().contains_key(key.data(py)))
307 }
309 }
308
310
309 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
311 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
310 let key = key.extract::<PyBytes>(py)?;
312 let key = key.extract::<PyBytes>(py)?;
311 let key = key.data(py);
313 let key = key.data(py);
312 match self.inner(py).borrow().get(key) {
314 match self.inner(py).borrow().get(key) {
313 Some(entry) => {
315 Some(entry) => {
314 // Explicitly go through u8 first, then cast to
316 // Explicitly go through u8 first, then cast to
315 // platform-specific `c_char`.
317 // platform-specific `c_char`.
316 let state: u8 = entry.state.into();
318 let state: u8 = entry.state.into();
317 Ok(decapsule_make_dirstate_tuple(py)?(
319 Ok(decapsule_make_dirstate_tuple(py)?(
318 state as c_char,
320 state as c_char,
319 entry.mode,
321 entry.mode,
320 entry.size,
322 entry.size,
321 entry.mtime,
323 entry.mtime,
322 ))
324 ))
323 },
325 },
324 None => Err(PyErr::new::<exc::KeyError, _>(
326 None => Err(PyErr::new::<exc::KeyError, _>(
325 py,
327 py,
326 String::from_utf8_lossy(key),
328 String::from_utf8_lossy(key),
327 )),
329 )),
328 }
330 }
329 }
331 }
330
332
331 def keys(&self) -> PyResult<DirstateMapKeysIterator> {
333 def keys(&self) -> PyResult<DirstateMapKeysIterator> {
332 DirstateMapKeysIterator::from_inner(
334 DirstateMapKeysIterator::from_inner(
333 py,
335 py,
334 Some(DirstateMapLeakedRef::new(py, &self)),
336 Some(DirstateMapLeakedRef::new(py, &self)),
335 Box::new(self.leak_immutable(py)?.iter()),
337 Box::new(self.leak_immutable(py)?.iter()),
336 )
338 )
337 }
339 }
338
340
339 def items(&self) -> PyResult<DirstateMapItemsIterator> {
341 def items(&self) -> PyResult<DirstateMapItemsIterator> {
340 DirstateMapItemsIterator::from_inner(
342 DirstateMapItemsIterator::from_inner(
341 py,
343 py,
342 Some(DirstateMapLeakedRef::new(py, &self)),
344 Some(DirstateMapLeakedRef::new(py, &self)),
343 Box::new(self.leak_immutable(py)?.iter()),
345 Box::new(self.leak_immutable(py)?.iter()),
344 )
346 )
345 }
347 }
346
348
347 def __iter__(&self) -> PyResult<DirstateMapKeysIterator> {
349 def __iter__(&self) -> PyResult<DirstateMapKeysIterator> {
348 DirstateMapKeysIterator::from_inner(
350 DirstateMapKeysIterator::from_inner(
349 py,
351 py,
350 Some(DirstateMapLeakedRef::new(py, &self)),
352 Some(DirstateMapLeakedRef::new(py, &self)),
351 Box::new(self.leak_immutable(py)?.iter()),
353 Box::new(self.leak_immutable(py)?.iter()),
352 )
354 )
353 }
355 }
354
356
355 def getdirs(&self) -> PyResult<Dirs> {
357 def getdirs(&self) -> PyResult<Dirs> {
356 // TODO don't copy, share the reference
358 // TODO don't copy, share the reference
357 self.inner(py).borrow_mut().set_dirs();
359 self.inner(py).borrow_mut().set_dirs();
358 Dirs::from_inner(
360 Dirs::from_inner(
359 py,
361 py,
360 DirsMultiset::new(
362 DirsMultiset::new(
361 DirsIterable::Dirstate(&self.inner(py).borrow()),
363 DirsIterable::Dirstate(&self.inner(py).borrow()),
362 Some(EntryState::Removed),
364 Some(EntryState::Removed),
363 ),
365 ),
364 )
366 )
365 }
367 }
366 def getalldirs(&self) -> PyResult<Dirs> {
368 def getalldirs(&self) -> PyResult<Dirs> {
367 // TODO don't copy, share the reference
369 // TODO don't copy, share the reference
368 self.inner(py).borrow_mut().set_all_dirs();
370 self.inner(py).borrow_mut().set_all_dirs();
369 Dirs::from_inner(
371 Dirs::from_inner(
370 py,
372 py,
371 DirsMultiset::new(
373 DirsMultiset::new(
372 DirsIterable::Dirstate(&self.inner(py).borrow()),
374 DirsIterable::Dirstate(&self.inner(py).borrow()),
373 None,
375 None,
374 ),
376 ),
375 )
377 )
376 }
378 }
377
379
378 // TODO all copymap* methods, see docstring above
380 // TODO all copymap* methods, see docstring above
379 def copymapcopy(&self) -> PyResult<PyDict> {
381 def copymapcopy(&self) -> PyResult<PyDict> {
380 let dict = PyDict::new(py);
382 let dict = PyDict::new(py);
381 for (key, value) in self.inner(py).borrow().copy_map.iter() {
383 for (key, value) in self.inner(py).borrow().copy_map.iter() {
382 dict.set_item(py, PyBytes::new(py, key), PyBytes::new(py, value))?;
384 dict.set_item(py, PyBytes::new(py, key), PyBytes::new(py, value))?;
383 }
385 }
384 Ok(dict)
386 Ok(dict)
385 }
387 }
386
388
387 def copymapgetitem(&self, key: PyObject) -> PyResult<PyBytes> {
389 def copymapgetitem(&self, key: PyObject) -> PyResult<PyBytes> {
388 let key = key.extract::<PyBytes>(py)?;
390 let key = key.extract::<PyBytes>(py)?;
389 match self.inner(py).borrow().copy_map.get(key.data(py)) {
391 match self.inner(py).borrow().copy_map.get(key.data(py)) {
390 Some(copy) => Ok(PyBytes::new(py, copy)),
392 Some(copy) => Ok(PyBytes::new(py, copy)),
391 None => Err(PyErr::new::<exc::KeyError, _>(
393 None => Err(PyErr::new::<exc::KeyError, _>(
392 py,
394 py,
393 String::from_utf8_lossy(key.data(py)),
395 String::from_utf8_lossy(key.data(py)),
394 )),
396 )),
395 }
397 }
396 }
398 }
397 def copymap(&self) -> PyResult<CopyMap> {
399 def copymap(&self) -> PyResult<CopyMap> {
398 CopyMap::from_inner(py, self.clone_ref(py))
400 CopyMap::from_inner(py, self.clone_ref(py))
399 }
401 }
400
402
401 def copymaplen(&self) -> PyResult<usize> {
403 def copymaplen(&self) -> PyResult<usize> {
402 Ok(self.inner(py).borrow().copy_map.len())
404 Ok(self.inner(py).borrow().copy_map.len())
403 }
405 }
404 def copymapcontains(&self, key: PyObject) -> PyResult<bool> {
406 def copymapcontains(&self, key: PyObject) -> PyResult<bool> {
405 let key = key.extract::<PyBytes>(py)?;
407 let key = key.extract::<PyBytes>(py)?;
406 Ok(self.inner(py).borrow().copy_map.contains_key(key.data(py)))
408 Ok(self.inner(py).borrow().copy_map.contains_key(key.data(py)))
407 }
409 }
408 def copymapget(
410 def copymapget(
409 &self,
411 &self,
410 key: PyObject,
412 key: PyObject,
411 default: Option<PyObject>
413 default: Option<PyObject>
412 ) -> PyResult<Option<PyObject>> {
414 ) -> PyResult<Option<PyObject>> {
413 let key = key.extract::<PyBytes>(py)?;
415 let key = key.extract::<PyBytes>(py)?;
414 match self.inner(py).borrow().copy_map.get(key.data(py)) {
416 match self.inner(py).borrow().copy_map.get(key.data(py)) {
415 Some(copy) => Ok(Some(PyBytes::new(py, copy).into_object())),
417 Some(copy) => Ok(Some(PyBytes::new(py, copy).into_object())),
416 None => Ok(default),
418 None => Ok(default),
417 }
419 }
418 }
420 }
419 def copymapsetitem(
421 def copymapsetitem(
420 &self,
422 &self,
421 key: PyObject,
423 key: PyObject,
422 value: PyObject
424 value: PyObject
423 ) -> PyResult<PyObject> {
425 ) -> PyResult<PyObject> {
424 let key = key.extract::<PyBytes>(py)?;
426 let key = key.extract::<PyBytes>(py)?;
425 let value = value.extract::<PyBytes>(py)?;
427 let value = value.extract::<PyBytes>(py)?;
426 self.inner(py)
428 self.inner(py)
427 .borrow_mut()
429 .borrow_mut()
428 .copy_map
430 .copy_map
429 .insert(key.data(py).to_vec(), value.data(py).to_vec());
431 .insert(key.data(py).to_vec(), value.data(py).to_vec());
430 Ok(py.None())
432 Ok(py.None())
431 }
433 }
432 def copymappop(
434 def copymappop(
433 &self,
435 &self,
434 key: PyObject,
436 key: PyObject,
435 default: Option<PyObject>
437 default: Option<PyObject>
436 ) -> PyResult<Option<PyObject>> {
438 ) -> PyResult<Option<PyObject>> {
437 let key = key.extract::<PyBytes>(py)?;
439 let key = key.extract::<PyBytes>(py)?;
438 match self.inner(py).borrow_mut().copy_map.remove(key.data(py)) {
440 match self.inner(py).borrow_mut().copy_map.remove(key.data(py)) {
439 Some(_) => Ok(None),
441 Some(_) => Ok(None),
440 None => Ok(default),
442 None => Ok(default),
441 }
443 }
442 }
444 }
443
445
444 def copymapiter(&self) -> PyResult<CopyMapKeysIterator> {
446 def copymapiter(&self) -> PyResult<CopyMapKeysIterator> {
445 CopyMapKeysIterator::from_inner(
447 CopyMapKeysIterator::from_inner(
446 py,
448 py,
447 Some(DirstateMapLeakedRef::new(py, &self)),
449 Some(DirstateMapLeakedRef::new(py, &self)),
448 Box::new(self.leak_immutable(py)?.copy_map.iter()),
450 Box::new(self.leak_immutable(py)?.copy_map.iter()),
449 )
451 )
450 }
452 }
451
453
452 def copymapitemsiter(&self) -> PyResult<CopyMapItemsIterator> {
454 def copymapitemsiter(&self) -> PyResult<CopyMapItemsIterator> {
453 CopyMapItemsIterator::from_inner(
455 CopyMapItemsIterator::from_inner(
454 py,
456 py,
455 Some(DirstateMapLeakedRef::new(py, &self)),
457 Some(DirstateMapLeakedRef::new(py, &self)),
456 Box::new(self.leak_immutable(py)?.copy_map.iter()),
458 Box::new(self.leak_immutable(py)?.copy_map.iter()),
457 )
459 )
458 }
460 }
459
461
460 });
462 });
461
463
462 impl DirstateMap {
464 impl DirstateMap {
463 fn translate_key(
465 fn translate_key(
464 py: Python,
466 py: Python,
465 res: (&Vec<u8>, &DirstateEntry),
467 res: (&Vec<u8>, &DirstateEntry),
466 ) -> PyResult<Option<PyBytes>> {
468 ) -> PyResult<Option<PyBytes>> {
467 Ok(Some(PyBytes::new(py, res.0)))
469 Ok(Some(PyBytes::new(py, res.0)))
468 }
470 }
469 fn translate_key_value(
471 fn translate_key_value(
470 py: Python,
472 py: Python,
471 res: (&Vec<u8>, &DirstateEntry),
473 res: (&Vec<u8>, &DirstateEntry),
472 ) -> PyResult<Option<(PyBytes, PyObject)>> {
474 ) -> PyResult<Option<(PyBytes, PyObject)>> {
473 let (f, entry) = res;
475 let (f, entry) = res;
474
476
475 // Explicitly go through u8 first, then cast to
477 // Explicitly go through u8 first, then cast to
476 // platform-specific `c_char`.
478 // platform-specific `c_char`.
477 let state: u8 = entry.state.into();
479 let state: u8 = entry.state.into();
478 Ok(Some((
480 Ok(Some((
479 PyBytes::new(py, f),
481 PyBytes::new(py, f),
480 decapsule_make_dirstate_tuple(py)?(
482 decapsule_make_dirstate_tuple(py)?(
481 state as c_char,
483 state as c_char,
482 entry.mode,
484 entry.mode,
483 entry.size,
485 entry.size,
484 entry.mtime,
486 entry.mtime,
485 ),
487 ),
486 )))
488 )))
487 }
489 }
488 }
490 }
489
491
490 py_shared_ref!(DirstateMap, RustDirstateMap, inner, DirstateMapLeakedRef,);
492 py_shared_ref!(DirstateMap, RustDirstateMap, inner, DirstateMapLeakedRef,);
491
493
492 py_shared_mapping_iterator!(
494 py_shared_mapping_iterator!(
493 DirstateMapKeysIterator,
495 DirstateMapKeysIterator,
494 DirstateMapLeakedRef,
496 DirstateMapLeakedRef,
495 Vec<u8>,
497 Vec<u8>,
496 DirstateEntry,
498 DirstateEntry,
497 DirstateMap::translate_key,
499 DirstateMap::translate_key,
498 Option<PyBytes>
500 Option<PyBytes>
499 );
501 );
500
502
501 py_shared_mapping_iterator!(
503 py_shared_mapping_iterator!(
502 DirstateMapItemsIterator,
504 DirstateMapItemsIterator,
503 DirstateMapLeakedRef,
505 DirstateMapLeakedRef,
504 Vec<u8>,
506 Vec<u8>,
505 DirstateEntry,
507 DirstateEntry,
506 DirstateMap::translate_key_value,
508 DirstateMap::translate_key_value,
507 Option<(PyBytes, PyObject)>
509 Option<(PyBytes, PyObject)>
508 );
510 );
@@ -1,207 +1,208
1 // parsers.rs
1 // parsers.rs
2 //
2 //
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 //
4 //
5 // This software may be used and distributed according to the terms of the
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
6 // GNU General Public License version 2 or any later version.
7
7
8 //! Bindings for the `hg::dirstate::parsers` module provided by the
8 //! Bindings for the `hg::dirstate::parsers` module provided by the
9 //! `hg-core` package.
9 //! `hg-core` package.
10 //!
10 //!
11 //! From Python, this will be seen as `mercurial.rustext.parsers`
11 //! From Python, this will be seen as `mercurial.rustext.parsers`
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 PythonObject, ToPyObject,
16 };
16 };
17 use hg::{
17 use hg::{
18 pack_dirstate, parse_dirstate, utils::copy_into_array, DirstateEntry,
18 pack_dirstate, parse_dirstate, DirstateEntry,
19 DirstatePackError, DirstateParents, DirstateParseError, PARENT_SIZE,
19 DirstatePackError, DirstateParents, DirstateParseError, PARENT_SIZE,
20 };
20 };
21 use std::collections::HashMap;
21 use std::collections::HashMap;
22 use std::convert::TryInto;
22
23
23 use libc::c_char;
24 use libc::c_char;
24
25
25 use crate::dirstate::{decapsule_make_dirstate_tuple, extract_dirstate};
26 use crate::dirstate::{decapsule_make_dirstate_tuple, extract_dirstate};
26 use std::time::Duration;
27 use std::time::Duration;
27
28
28 fn parse_dirstate_wrapper(
29 fn parse_dirstate_wrapper(
29 py: Python,
30 py: Python,
30 dmap: PyDict,
31 dmap: PyDict,
31 copymap: PyDict,
32 copymap: PyDict,
32 st: PyBytes,
33 st: PyBytes,
33 ) -> PyResult<PyTuple> {
34 ) -> PyResult<PyTuple> {
34 let mut dirstate_map = HashMap::new();
35 let mut dirstate_map = HashMap::new();
35 let mut copies = HashMap::new();
36 let mut copies = HashMap::new();
36
37
37 match parse_dirstate(&mut dirstate_map, &mut copies, st.data(py)) {
38 match parse_dirstate(&mut dirstate_map, &mut copies, st.data(py)) {
38 Ok(parents) => {
39 Ok(parents) => {
39 for (filename, entry) in dirstate_map {
40 for (filename, entry) in dirstate_map {
40 // Explicitly go through u8 first, then cast to
41 // Explicitly go through u8 first, then cast to
41 // platform-specific `c_char` because Into<u8> has a specific
42 // platform-specific `c_char` because Into<u8> has a specific
42 // implementation while `as c_char` would just do a naive enum
43 // implementation while `as c_char` would just do a naive enum
43 // cast.
44 // cast.
44 let state: u8 = entry.state.into();
45 let state: u8 = entry.state.into();
45
46
46 dmap.set_item(
47 dmap.set_item(
47 py,
48 py,
48 PyBytes::new(py, &filename),
49 PyBytes::new(py, &filename),
49 decapsule_make_dirstate_tuple(py)?(
50 decapsule_make_dirstate_tuple(py)?(
50 state as c_char,
51 state as c_char,
51 entry.mode,
52 entry.mode,
52 entry.size,
53 entry.size,
53 entry.mtime,
54 entry.mtime,
54 ),
55 ),
55 )?;
56 )?;
56 }
57 }
57 for (path, copy_path) in copies {
58 for (path, copy_path) in copies {
58 copymap.set_item(
59 copymap.set_item(
59 py,
60 py,
60 PyBytes::new(py, &path),
61 PyBytes::new(py, &path),
61 PyBytes::new(py, &copy_path),
62 PyBytes::new(py, &copy_path),
62 )?;
63 )?;
63 }
64 }
64 Ok(
65 Ok(
65 (PyBytes::new(py, &parents.p1), PyBytes::new(py, &parents.p2))
66 (PyBytes::new(py, &parents.p1), PyBytes::new(py, &parents.p2))
66 .to_py_object(py),
67 .to_py_object(py),
67 )
68 )
68 }
69 }
69 Err(e) => Err(PyErr::new::<exc::ValueError, _>(
70 Err(e) => Err(PyErr::new::<exc::ValueError, _>(
70 py,
71 py,
71 match e {
72 match e {
72 DirstateParseError::TooLittleData => {
73 DirstateParseError::TooLittleData => {
73 "too little data for parents".to_string()
74 "too little data for parents".to_string()
74 }
75 }
75 DirstateParseError::Overflow => {
76 DirstateParseError::Overflow => {
76 "overflow in dirstate".to_string()
77 "overflow in dirstate".to_string()
77 }
78 }
78 DirstateParseError::CorruptedEntry(e) => e,
79 DirstateParseError::CorruptedEntry(e) => e,
79 DirstateParseError::Damaged => {
80 DirstateParseError::Damaged => {
80 "dirstate appears to be damaged".to_string()
81 "dirstate appears to be damaged".to_string()
81 }
82 }
82 },
83 },
83 )),
84 )),
84 }
85 }
85 }
86 }
86
87
87 fn pack_dirstate_wrapper(
88 fn pack_dirstate_wrapper(
88 py: Python,
89 py: Python,
89 dmap: PyDict,
90 dmap: PyDict,
90 copymap: PyDict,
91 copymap: PyDict,
91 pl: PyTuple,
92 pl: PyTuple,
92 now: PyInt,
93 now: PyInt,
93 ) -> PyResult<PyBytes> {
94 ) -> PyResult<PyBytes> {
94 let p1 = pl.get_item(py, 0).extract::<PyBytes>(py)?;
95 let p1 = pl.get_item(py, 0).extract::<PyBytes>(py)?;
95 let p1: &[u8] = p1.data(py);
96 let p1: &[u8] = p1.data(py);
96 let p2 = pl.get_item(py, 1).extract::<PyBytes>(py)?;
97 let p2 = pl.get_item(py, 1).extract::<PyBytes>(py)?;
97 let p2: &[u8] = p2.data(py);
98 let p2: &[u8] = p2.data(py);
98
99
99 let mut dirstate_map = extract_dirstate(py, &dmap)?;
100 let mut dirstate_map = extract_dirstate(py, &dmap)?;
100
101
101 let copies: Result<HashMap<Vec<u8>, Vec<u8>>, PyErr> = copymap
102 let copies: Result<HashMap<Vec<u8>, Vec<u8>>, PyErr> = copymap
102 .items(py)
103 .items(py)
103 .iter()
104 .iter()
104 .map(|(key, value)| {
105 .map(|(key, value)| {
105 Ok((
106 Ok((
106 key.extract::<PyBytes>(py)?.data(py).to_owned(),
107 key.extract::<PyBytes>(py)?.data(py).to_owned(),
107 value.extract::<PyBytes>(py)?.data(py).to_owned(),
108 value.extract::<PyBytes>(py)?.data(py).to_owned(),
108 ))
109 ))
109 })
110 })
110 .collect();
111 .collect();
111
112
112 if p1.len() != PARENT_SIZE || p2.len() != PARENT_SIZE {
113 if p1.len() != PARENT_SIZE || p2.len() != PARENT_SIZE {
113 return Err(PyErr::new::<exc::ValueError, _>(
114 return Err(PyErr::new::<exc::ValueError, _>(
114 py,
115 py,
115 "expected a 20-byte hash".to_string(),
116 "expected a 20-byte hash".to_string(),
116 ));
117 ));
117 }
118 }
118
119
119 match pack_dirstate(
120 match pack_dirstate(
120 &mut dirstate_map,
121 &mut dirstate_map,
121 &copies?,
122 &copies?,
122 DirstateParents {
123 DirstateParents {
123 p1: copy_into_array(&p1),
124 p1: p1.try_into().unwrap(),
124 p2: copy_into_array(&p2),
125 p2: p2.try_into().unwrap(),
125 },
126 },
126 Duration::from_secs(now.as_object().extract::<u64>(py)?),
127 Duration::from_secs(now.as_object().extract::<u64>(py)?),
127 ) {
128 ) {
128 Ok(packed) => {
129 Ok(packed) => {
129 for (
130 for (
130 filename,
131 filename,
131 DirstateEntry {
132 DirstateEntry {
132 state,
133 state,
133 mode,
134 mode,
134 size,
135 size,
135 mtime,
136 mtime,
136 },
137 },
137 ) in dirstate_map
138 ) in dirstate_map
138 {
139 {
139 // Explicitly go through u8 first, then cast to
140 // Explicitly go through u8 first, then cast to
140 // platform-specific `c_char` because Into<u8> has a specific
141 // platform-specific `c_char` because Into<u8> has a specific
141 // implementation while `as c_char` would just do a naive enum
142 // implementation while `as c_char` would just do a naive enum
142 // cast.
143 // cast.
143 let state: u8 = state.into();
144 let state: u8 = state.into();
144 dmap.set_item(
145 dmap.set_item(
145 py,
146 py,
146 PyBytes::new(py, &filename[..]),
147 PyBytes::new(py, &filename[..]),
147 decapsule_make_dirstate_tuple(py)?(
148 decapsule_make_dirstate_tuple(py)?(
148 state as c_char,
149 state as c_char,
149 mode,
150 mode,
150 size,
151 size,
151 mtime,
152 mtime,
152 ),
153 ),
153 )?;
154 )?;
154 }
155 }
155 Ok(PyBytes::new(py, &packed))
156 Ok(PyBytes::new(py, &packed))
156 }
157 }
157 Err(error) => Err(PyErr::new::<exc::ValueError, _>(
158 Err(error) => Err(PyErr::new::<exc::ValueError, _>(
158 py,
159 py,
159 match error {
160 match error {
160 DirstatePackError::CorruptedParent => {
161 DirstatePackError::CorruptedParent => {
161 "expected a 20-byte hash".to_string()
162 "expected a 20-byte hash".to_string()
162 }
163 }
163 DirstatePackError::CorruptedEntry(e) => e,
164 DirstatePackError::CorruptedEntry(e) => e,
164 DirstatePackError::BadSize(expected, actual) => {
165 DirstatePackError::BadSize(expected, actual) => {
165 format!("bad dirstate size: {} != {}", actual, expected)
166 format!("bad dirstate size: {} != {}", actual, expected)
166 }
167 }
167 },
168 },
168 )),
169 )),
169 }
170 }
170 }
171 }
171
172
172 /// Create the module, with `__package__` given from parent
173 /// Create the module, with `__package__` given from parent
173 pub fn init_parsers_module(py: Python, package: &str) -> PyResult<PyModule> {
174 pub fn init_parsers_module(py: Python, package: &str) -> PyResult<PyModule> {
174 let dotted_name = &format!("{}.parsers", package);
175 let dotted_name = &format!("{}.parsers", package);
175 let m = PyModule::new(py, dotted_name)?;
176 let m = PyModule::new(py, dotted_name)?;
176
177
177 m.add(py, "__package__", package)?;
178 m.add(py, "__package__", package)?;
178 m.add(py, "__doc__", "Parsers - Rust implementation")?;
179 m.add(py, "__doc__", "Parsers - Rust implementation")?;
179
180
180 m.add(
181 m.add(
181 py,
182 py,
182 "parse_dirstate",
183 "parse_dirstate",
183 py_fn!(
184 py_fn!(
184 py,
185 py,
185 parse_dirstate_wrapper(dmap: PyDict, copymap: PyDict, st: PyBytes)
186 parse_dirstate_wrapper(dmap: PyDict, copymap: PyDict, st: PyBytes)
186 ),
187 ),
187 )?;
188 )?;
188 m.add(
189 m.add(
189 py,
190 py,
190 "pack_dirstate",
191 "pack_dirstate",
191 py_fn!(
192 py_fn!(
192 py,
193 py,
193 pack_dirstate_wrapper(
194 pack_dirstate_wrapper(
194 dmap: PyDict,
195 dmap: PyDict,
195 copymap: PyDict,
196 copymap: PyDict,
196 pl: PyTuple,
197 pl: PyTuple,
197 now: PyInt
198 now: PyInt
198 )
199 )
199 ),
200 ),
200 )?;
201 )?;
201
202
202 let sys = PyModule::import(py, "sys")?;
203 let sys = PyModule::import(py, "sys")?;
203 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
204 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
204 sys_modules.set_item(py, dotted_name, &m)?;
205 sys_modules.set_item(py, dotted_name, &m)?;
205
206
206 Ok(m)
207 Ok(m)
207 }
208 }
General Comments 0
You need to be logged in to leave comments. Login now