##// END OF EJS Templates
rust-dirstate: handle invalid length of p1/p2 parameters...
Yuya Nishihara -
r43068:79561843 default
parent child Browse files
Show More
@@ -1,510 +1,515 b''
1 1 // dirstate_map.rs
2 2 //
3 3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 4 //
5 5 // This software may be used and distributed according to the terms of the
6 6 // GNU General Public License version 2 or any later version.
7 7
8 8 //! Bindings for the `hg::dirstate::dirstate_map` file provided by the
9 9 //! `hg-core` package.
10 10
11 11 use std::cell::RefCell;
12 12 use std::convert::TryInto;
13 13 use std::time::Duration;
14 14
15 15 use cpython::{
16 16 exc, ObjectProtocol, PyBool, PyBytes, PyClone, PyDict, PyErr, PyObject,
17 17 PyResult, PyTuple, Python, PythonObject, ToPyObject,
18 18 };
19 19 use libc::c_char;
20 20
21 21 use crate::{
22 22 dirstate::copymap::{CopyMap, CopyMapItemsIterator, CopyMapKeysIterator},
23 23 dirstate::{decapsule_make_dirstate_tuple, dirs_multiset::Dirs},
24 24 ref_sharing::PySharedState,
25 25 };
26 26 use hg::{
27 DirsIterable, DirsMultiset, DirstateEntry,
28 DirstateMap as RustDirstateMap, DirstateParents, DirstateParseError,
29 EntryState,
27 DirsIterable, DirsMultiset, DirstateEntry, DirstateMap as RustDirstateMap,
28 DirstateParents, DirstateParseError, EntryState, PARENT_SIZE,
30 29 };
31 30
32 31 // TODO
33 32 // This object needs to share references to multiple members of its Rust
34 33 // inner struct, namely `copy_map`, `dirs` and `all_dirs`.
35 34 // Right now `CopyMap` is done, but it needs to have an explicit reference
36 35 // to `RustDirstateMap` which itself needs to have an encapsulation for
37 36 // every method in `CopyMap` (copymapcopy, etc.).
38 37 // This is ugly and hard to maintain.
39 38 // The same logic applies to `dirs` and `all_dirs`, however the `Dirs`
40 39 // `py_class!` is already implemented and does not mention
41 40 // `RustDirstateMap`, rightfully so.
42 41 // All attributes also have to have a separate refcount data attribute for
43 42 // leaks, with all methods that go along for reference sharing.
44 43 py_class!(pub class DirstateMap |py| {
45 44 data inner: RefCell<RustDirstateMap>;
46 45 data py_shared_state: PySharedState;
47 46
48 47 def __new__(_cls, _root: PyObject) -> PyResult<Self> {
49 48 let inner = RustDirstateMap::default();
50 49 Self::create_instance(
51 50 py,
52 51 RefCell::new(inner),
53 52 PySharedState::default()
54 53 )
55 54 }
56 55
57 56 def clear(&self) -> PyResult<PyObject> {
58 57 self.borrow_mut(py)?.clear();
59 58 Ok(py.None())
60 59 }
61 60
62 61 def get(
63 62 &self,
64 63 key: PyObject,
65 64 default: Option<PyObject> = None
66 65 ) -> PyResult<Option<PyObject>> {
67 66 let key = key.extract::<PyBytes>(py)?;
68 67 match self.inner(py).borrow().get(key.data(py)) {
69 68 Some(entry) => {
70 69 // Explicitly go through u8 first, then cast to
71 70 // platform-specific `c_char`.
72 71 let state: u8 = entry.state.into();
73 72 Ok(Some(decapsule_make_dirstate_tuple(py)?(
74 73 state as c_char,
75 74 entry.mode,
76 75 entry.size,
77 76 entry.mtime,
78 77 )))
79 78 },
80 79 None => Ok(default)
81 80 }
82 81 }
83 82
84 83 def addfile(
85 84 &self,
86 85 f: PyObject,
87 86 oldstate: PyObject,
88 87 state: PyObject,
89 88 mode: PyObject,
90 89 size: PyObject,
91 90 mtime: PyObject
92 91 ) -> PyResult<PyObject> {
93 92 self.borrow_mut(py)?.add_file(
94 93 f.extract::<PyBytes>(py)?.data(py),
95 94 oldstate.extract::<PyBytes>(py)?.data(py)[0]
96 95 .try_into()
97 96 .map_err(|e: DirstateParseError| {
98 97 PyErr::new::<exc::ValueError, _>(py, e.to_string())
99 98 })?,
100 99 DirstateEntry {
101 100 state: state.extract::<PyBytes>(py)?.data(py)[0]
102 101 .try_into()
103 102 .map_err(|e: DirstateParseError| {
104 103 PyErr::new::<exc::ValueError, _>(py, e.to_string())
105 104 })?,
106 105 mode: mode.extract(py)?,
107 106 size: size.extract(py)?,
108 107 mtime: mtime.extract(py)?,
109 108 },
110 109 );
111 110 Ok(py.None())
112 111 }
113 112
114 113 def removefile(
115 114 &self,
116 115 f: PyObject,
117 116 oldstate: PyObject,
118 117 size: PyObject
119 118 ) -> PyResult<PyObject> {
120 119 self.borrow_mut(py)?
121 120 .remove_file(
122 121 f.extract::<PyBytes>(py)?.data(py),
123 122 oldstate.extract::<PyBytes>(py)?.data(py)[0]
124 123 .try_into()
125 124 .map_err(|e: DirstateParseError| {
126 125 PyErr::new::<exc::ValueError, _>(py, e.to_string())
127 126 })?,
128 127 size.extract(py)?,
129 128 )
130 129 .or_else(|_| {
131 130 Err(PyErr::new::<exc::OSError, _>(
132 131 py,
133 132 "Dirstate error".to_string(),
134 133 ))
135 134 })?;
136 135 Ok(py.None())
137 136 }
138 137
139 138 def dropfile(
140 139 &self,
141 140 f: PyObject,
142 141 oldstate: PyObject
143 142 ) -> PyResult<PyBool> {
144 143 self.borrow_mut(py)?
145 144 .drop_file(
146 145 f.extract::<PyBytes>(py)?.data(py),
147 146 oldstate.extract::<PyBytes>(py)?.data(py)[0]
148 147 .try_into()
149 148 .map_err(|e: DirstateParseError| {
150 149 PyErr::new::<exc::ValueError, _>(py, e.to_string())
151 150 })?,
152 151 )
153 152 .and_then(|b| Ok(b.to_py_object(py)))
154 153 .or_else(|_| {
155 154 Err(PyErr::new::<exc::OSError, _>(
156 155 py,
157 156 "Dirstate error".to_string(),
158 157 ))
159 158 })
160 159 }
161 160
162 161 def clearambiguoustimes(
163 162 &self,
164 163 files: PyObject,
165 164 now: PyObject
166 165 ) -> PyResult<PyObject> {
167 166 let files: PyResult<Vec<Vec<u8>>> = files
168 167 .iter(py)?
169 168 .map(|filename| {
170 169 Ok(filename?.extract::<PyBytes>(py)?.data(py).to_owned())
171 170 })
172 171 .collect();
173 172 self.inner(py)
174 173 .borrow_mut()
175 174 .clear_ambiguous_times(files?, now.extract(py)?);
176 175 Ok(py.None())
177 176 }
178 177
179 178 // TODO share the reference
180 179 def nonnormalentries(&self) -> PyResult<PyObject> {
181 180 let (non_normal, other_parent) =
182 181 self.inner(py).borrow().non_normal_other_parent_entries();
183 182
184 183 let locals = PyDict::new(py);
185 184 locals.set_item(
186 185 py,
187 186 "non_normal",
188 187 non_normal
189 188 .iter()
190 189 .map(|v| PyBytes::new(py, &v))
191 190 .collect::<Vec<PyBytes>>()
192 191 .to_py_object(py),
193 192 )?;
194 193 locals.set_item(
195 194 py,
196 195 "other_parent",
197 196 other_parent
198 197 .iter()
199 198 .map(|v| PyBytes::new(py, &v))
200 199 .collect::<Vec<PyBytes>>()
201 200 .to_py_object(py),
202 201 )?;
203 202
204 203 py.eval("set(non_normal), set(other_parent)", None, Some(&locals))
205 204 }
206 205
207 206 def hastrackeddir(&self, d: PyObject) -> PyResult<PyBool> {
208 207 let d = d.extract::<PyBytes>(py)?;
209 208 Ok(self
210 209 .inner(py)
211 210 .borrow_mut()
212 211 .has_tracked_dir(d.data(py))
213 212 .to_py_object(py))
214 213 }
215 214
216 215 def hasdir(&self, d: PyObject) -> PyResult<PyBool> {
217 216 let d = d.extract::<PyBytes>(py)?;
218 217 Ok(self
219 218 .inner(py)
220 219 .borrow_mut()
221 220 .has_dir(d.data(py))
222 221 .to_py_object(py))
223 222 }
224 223
225 224 def parents(&self, st: PyObject) -> PyResult<PyTuple> {
226 225 self.inner(py)
227 226 .borrow_mut()
228 227 .parents(st.extract::<PyBytes>(py)?.data(py))
229 228 .and_then(|d| {
230 229 Ok((PyBytes::new(py, &d.p1), PyBytes::new(py, &d.p2))
231 230 .to_py_object(py))
232 231 })
233 232 .or_else(|_| {
234 233 Err(PyErr::new::<exc::OSError, _>(
235 234 py,
236 235 "Dirstate error".to_string(),
237 236 ))
238 237 })
239 238 }
240 239
241 240 def setparents(&self, p1: PyObject, p2: PyObject) -> PyResult<PyObject> {
242 // TODO: don't panic; raise Python exception instead.
243 let p1 = p1.extract::<PyBytes>(py)?.data(py).try_into().unwrap();
244 let p2 = p2.extract::<PyBytes>(py)?.data(py).try_into().unwrap();
241 let p1 = extract_node_id(py, &p1)?;
242 let p2 = extract_node_id(py, &p2)?;
245 243
246 244 self.inner(py)
247 245 .borrow_mut()
248 246 .set_parents(DirstateParents { p1, p2 });
249 247 Ok(py.None())
250 248 }
251 249
252 250 def read(&self, st: PyObject) -> PyResult<Option<PyObject>> {
253 251 match self
254 252 .inner(py)
255 253 .borrow_mut()
256 254 .read(st.extract::<PyBytes>(py)?.data(py))
257 255 {
258 256 Ok(Some(parents)) => Ok(Some(
259 257 (PyBytes::new(py, &parents.p1), PyBytes::new(py, &parents.p2))
260 258 .to_py_object(py)
261 259 .into_object(),
262 260 )),
263 261 Ok(None) => Ok(Some(py.None())),
264 262 Err(_) => Err(PyErr::new::<exc::OSError, _>(
265 263 py,
266 264 "Dirstate error".to_string(),
267 265 )),
268 266 }
269 267 }
270 268 def write(
271 269 &self,
272 270 p1: PyObject,
273 271 p2: PyObject,
274 272 now: PyObject
275 273 ) -> PyResult<PyBytes> {
276 274 let now = Duration::new(now.extract(py)?, 0);
277 275 let parents = DirstateParents {
278 // TODO: don't panic; raise Python exception instead.
279 p1: p1.extract::<PyBytes>(py)?.data(py).try_into().unwrap(),
280 p2: p2.extract::<PyBytes>(py)?.data(py).try_into().unwrap(),
276 p1: extract_node_id(py, &p1)?,
277 p2: extract_node_id(py, &p2)?,
281 278 };
282 279
283 280 match self.borrow_mut(py)?.pack(parents, now) {
284 281 Ok(packed) => Ok(PyBytes::new(py, &packed)),
285 282 Err(_) => Err(PyErr::new::<exc::OSError, _>(
286 283 py,
287 284 "Dirstate error".to_string(),
288 285 )),
289 286 }
290 287 }
291 288
292 289 def filefoldmapasdict(&self) -> PyResult<PyDict> {
293 290 let dict = PyDict::new(py);
294 291 for (key, value) in
295 292 self.borrow_mut(py)?.build_file_fold_map().iter()
296 293 {
297 294 dict.set_item(py, key, value)?;
298 295 }
299 296 Ok(dict)
300 297 }
301 298
302 299 def __len__(&self) -> PyResult<usize> {
303 300 Ok(self.inner(py).borrow().len())
304 301 }
305 302
306 303 def __contains__(&self, key: PyObject) -> PyResult<bool> {
307 304 let key = key.extract::<PyBytes>(py)?;
308 305 Ok(self.inner(py).borrow().contains_key(key.data(py)))
309 306 }
310 307
311 308 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
312 309 let key = key.extract::<PyBytes>(py)?;
313 310 let key = key.data(py);
314 311 match self.inner(py).borrow().get(key) {
315 312 Some(entry) => {
316 313 // Explicitly go through u8 first, then cast to
317 314 // platform-specific `c_char`.
318 315 let state: u8 = entry.state.into();
319 316 Ok(decapsule_make_dirstate_tuple(py)?(
320 317 state as c_char,
321 318 entry.mode,
322 319 entry.size,
323 320 entry.mtime,
324 321 ))
325 322 },
326 323 None => Err(PyErr::new::<exc::KeyError, _>(
327 324 py,
328 325 String::from_utf8_lossy(key),
329 326 )),
330 327 }
331 328 }
332 329
333 330 def keys(&self) -> PyResult<DirstateMapKeysIterator> {
334 331 DirstateMapKeysIterator::from_inner(
335 332 py,
336 333 Some(DirstateMapLeakedRef::new(py, &self)),
337 334 Box::new(self.leak_immutable(py)?.iter()),
338 335 )
339 336 }
340 337
341 338 def items(&self) -> PyResult<DirstateMapItemsIterator> {
342 339 DirstateMapItemsIterator::from_inner(
343 340 py,
344 341 Some(DirstateMapLeakedRef::new(py, &self)),
345 342 Box::new(self.leak_immutable(py)?.iter()),
346 343 )
347 344 }
348 345
349 346 def __iter__(&self) -> PyResult<DirstateMapKeysIterator> {
350 347 DirstateMapKeysIterator::from_inner(
351 348 py,
352 349 Some(DirstateMapLeakedRef::new(py, &self)),
353 350 Box::new(self.leak_immutable(py)?.iter()),
354 351 )
355 352 }
356 353
357 354 def getdirs(&self) -> PyResult<Dirs> {
358 355 // TODO don't copy, share the reference
359 356 self.inner(py).borrow_mut().set_dirs();
360 357 Dirs::from_inner(
361 358 py,
362 359 DirsMultiset::new(
363 360 DirsIterable::Dirstate(&self.inner(py).borrow()),
364 361 Some(EntryState::Removed),
365 362 ),
366 363 )
367 364 }
368 365 def getalldirs(&self) -> PyResult<Dirs> {
369 366 // TODO don't copy, share the reference
370 367 self.inner(py).borrow_mut().set_all_dirs();
371 368 Dirs::from_inner(
372 369 py,
373 370 DirsMultiset::new(
374 371 DirsIterable::Dirstate(&self.inner(py).borrow()),
375 372 None,
376 373 ),
377 374 )
378 375 }
379 376
380 377 // TODO all copymap* methods, see docstring above
381 378 def copymapcopy(&self) -> PyResult<PyDict> {
382 379 let dict = PyDict::new(py);
383 380 for (key, value) in self.inner(py).borrow().copy_map.iter() {
384 381 dict.set_item(py, PyBytes::new(py, key), PyBytes::new(py, value))?;
385 382 }
386 383 Ok(dict)
387 384 }
388 385
389 386 def copymapgetitem(&self, key: PyObject) -> PyResult<PyBytes> {
390 387 let key = key.extract::<PyBytes>(py)?;
391 388 match self.inner(py).borrow().copy_map.get(key.data(py)) {
392 389 Some(copy) => Ok(PyBytes::new(py, copy)),
393 390 None => Err(PyErr::new::<exc::KeyError, _>(
394 391 py,
395 392 String::from_utf8_lossy(key.data(py)),
396 393 )),
397 394 }
398 395 }
399 396 def copymap(&self) -> PyResult<CopyMap> {
400 397 CopyMap::from_inner(py, self.clone_ref(py))
401 398 }
402 399
403 400 def copymaplen(&self) -> PyResult<usize> {
404 401 Ok(self.inner(py).borrow().copy_map.len())
405 402 }
406 403 def copymapcontains(&self, key: PyObject) -> PyResult<bool> {
407 404 let key = key.extract::<PyBytes>(py)?;
408 405 Ok(self.inner(py).borrow().copy_map.contains_key(key.data(py)))
409 406 }
410 407 def copymapget(
411 408 &self,
412 409 key: PyObject,
413 410 default: Option<PyObject>
414 411 ) -> PyResult<Option<PyObject>> {
415 412 let key = key.extract::<PyBytes>(py)?;
416 413 match self.inner(py).borrow().copy_map.get(key.data(py)) {
417 414 Some(copy) => Ok(Some(PyBytes::new(py, copy).into_object())),
418 415 None => Ok(default),
419 416 }
420 417 }
421 418 def copymapsetitem(
422 419 &self,
423 420 key: PyObject,
424 421 value: PyObject
425 422 ) -> PyResult<PyObject> {
426 423 let key = key.extract::<PyBytes>(py)?;
427 424 let value = value.extract::<PyBytes>(py)?;
428 425 self.inner(py)
429 426 .borrow_mut()
430 427 .copy_map
431 428 .insert(key.data(py).to_vec(), value.data(py).to_vec());
432 429 Ok(py.None())
433 430 }
434 431 def copymappop(
435 432 &self,
436 433 key: PyObject,
437 434 default: Option<PyObject>
438 435 ) -> PyResult<Option<PyObject>> {
439 436 let key = key.extract::<PyBytes>(py)?;
440 437 match self.inner(py).borrow_mut().copy_map.remove(key.data(py)) {
441 438 Some(_) => Ok(None),
442 439 None => Ok(default),
443 440 }
444 441 }
445 442
446 443 def copymapiter(&self) -> PyResult<CopyMapKeysIterator> {
447 444 CopyMapKeysIterator::from_inner(
448 445 py,
449 446 Some(DirstateMapLeakedRef::new(py, &self)),
450 447 Box::new(self.leak_immutable(py)?.copy_map.iter()),
451 448 )
452 449 }
453 450
454 451 def copymapitemsiter(&self) -> PyResult<CopyMapItemsIterator> {
455 452 CopyMapItemsIterator::from_inner(
456 453 py,
457 454 Some(DirstateMapLeakedRef::new(py, &self)),
458 455 Box::new(self.leak_immutable(py)?.copy_map.iter()),
459 456 )
460 457 }
461 458
462 459 });
463 460
464 461 impl DirstateMap {
465 462 fn translate_key(
466 463 py: Python,
467 464 res: (&Vec<u8>, &DirstateEntry),
468 465 ) -> PyResult<Option<PyBytes>> {
469 466 Ok(Some(PyBytes::new(py, res.0)))
470 467 }
471 468 fn translate_key_value(
472 469 py: Python,
473 470 res: (&Vec<u8>, &DirstateEntry),
474 471 ) -> PyResult<Option<(PyBytes, PyObject)>> {
475 472 let (f, entry) = res;
476 473
477 474 // Explicitly go through u8 first, then cast to
478 475 // platform-specific `c_char`.
479 476 let state: u8 = entry.state.into();
480 477 Ok(Some((
481 478 PyBytes::new(py, f),
482 479 decapsule_make_dirstate_tuple(py)?(
483 480 state as c_char,
484 481 entry.mode,
485 482 entry.size,
486 483 entry.mtime,
487 484 ),
488 485 )))
489 486 }
490 487 }
491 488
492 489 py_shared_ref!(DirstateMap, RustDirstateMap, inner, DirstateMapLeakedRef,);
493 490
494 491 py_shared_mapping_iterator!(
495 492 DirstateMapKeysIterator,
496 493 DirstateMapLeakedRef,
497 494 Vec<u8>,
498 495 DirstateEntry,
499 496 DirstateMap::translate_key,
500 497 Option<PyBytes>
501 498 );
502 499
503 500 py_shared_mapping_iterator!(
504 501 DirstateMapItemsIterator,
505 502 DirstateMapLeakedRef,
506 503 Vec<u8>,
507 504 DirstateEntry,
508 505 DirstateMap::translate_key_value,
509 506 Option<(PyBytes, PyObject)>
510 507 );
508
509 fn extract_node_id(py: Python, obj: &PyObject) -> PyResult<[u8; PARENT_SIZE]> {
510 let bytes = obj.extract::<PyBytes>(py)?;
511 match bytes.data(py).try_into() {
512 Ok(s) => Ok(s),
513 Err(e) => Err(PyErr::new::<exc::ValueError, _>(py, e.to_string())),
514 }
515 }
General Comments 0
You need to be logged in to leave comments. Login now