##// END OF EJS Templates
rust-dirstate: add rust-cpython bindings to the new parse/pack functions...
Raphaël Gomès -
r42489:e240bec2 default
parent child Browse files
Show More
@@ -0,0 +1,227 b''
1 // dirstate.rs
2 //
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 //
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
7
8 //! Bindings for the `hg::dirstate` module provided by the
9 //! `hg-core` package.
10 //!
11 //! From Python, this will be seen as `mercurial.rustext.dirstate`
12
13 use cpython::{
14 exc, PyBytes, PyDict, PyErr, PyInt, PyModule, PyObject, PyResult,
15 PySequence, PyTuple, Python, ToPyObject,
16 };
17 use hg::{
18 pack_dirstate, parse_dirstate, CopyVecEntry, DirstateEntry,
19 DirstatePackError, DirstateParents, DirstateParseError, DirstateVec,
20 };
21 use std::collections::HashMap;
22 use std::ffi::CStr;
23 #[cfg(feature = "python27")]
24 extern crate python27_sys as python_sys;
25 #[cfg(feature = "python3")]
26 extern crate python3_sys as python_sys;
27 use self::python_sys::PyCapsule_Import;
28 use libc::{c_char, c_int};
29 use std::mem::transmute;
30
31 /// C code uses a custom `dirstate_tuple` type, checks in multiple instances
32 /// for this type, and raises a Python `Exception` if the check does not pass.
33 /// Because this type differs only in name from the regular Python tuple, it
34 /// would be a good idea in the near future to remove it entirely to allow
35 /// for a pure Python tuple of the same effective structure to be used,
36 /// rendering this type and the capsule below useless.
37 type MakeDirstateTupleFn = extern "C" fn(
38 state: c_char,
39 mode: c_int,
40 size: c_int,
41 mtime: c_int,
42 ) -> PyObject;
43
44 /// This is largely a copy/paste from cindex.rs, pending the merge of a
45 /// `py_capsule_fn!` macro in the rust-cpython project:
46 /// https://github.com/dgrunwald/rust-cpython/pull/169
47 fn decapsule_make_dirstate_tuple(py: Python) -> PyResult<MakeDirstateTupleFn> {
48 unsafe {
49 let caps_name = CStr::from_bytes_with_nul_unchecked(
50 b"mercurial.cext.parsers.make_dirstate_tuple_CAPI\0",
51 );
52 let from_caps = PyCapsule_Import(caps_name.as_ptr(), 0);
53 if from_caps.is_null() {
54 return Err(PyErr::fetch(py));
55 }
56 Ok(transmute(from_caps))
57 }
58 }
59
60 fn parse_dirstate_wrapper(
61 py: Python,
62 dmap: PyDict,
63 copymap: PyDict,
64 st: PyBytes,
65 ) -> PyResult<PyTuple> {
66 match parse_dirstate(st.data(py)) {
67 Ok((parents, dirstate_vec, copies)) => {
68 for (filename, entry) in dirstate_vec {
69 dmap.set_item(
70 py,
71 PyBytes::new(py, &filename[..]),
72 decapsule_make_dirstate_tuple(py)?(
73 entry.state,
74 entry.mode,
75 entry.size,
76 entry.mtime,
77 ),
78 )?;
79 }
80 for CopyVecEntry { path, copy_path } in copies {
81 copymap.set_item(
82 py,
83 PyBytes::new(py, path),
84 PyBytes::new(py, copy_path),
85 )?;
86 }
87 Ok((PyBytes::new(py, parents.p1), PyBytes::new(py, parents.p2))
88 .to_py_object(py))
89 }
90 Err(e) => Err(PyErr::new::<exc::ValueError, _>(
91 py,
92 match e {
93 DirstateParseError::TooLittleData => {
94 "too little data for parents".to_string()
95 }
96 DirstateParseError::Overflow => {
97 "overflow in dirstate".to_string()
98 }
99 DirstateParseError::CorruptedEntry(e) => e,
100 },
101 )),
102 }
103 }
104
105 fn pack_dirstate_wrapper(
106 py: Python,
107 dmap: PyDict,
108 copymap: PyDict,
109 pl: PyTuple,
110 now: PyInt,
111 ) -> PyResult<PyBytes> {
112 let p1 = pl.get_item(py, 0).extract::<PyBytes>(py)?;
113 let p1: &[u8] = p1.data(py);
114 let p2 = pl.get_item(py, 1).extract::<PyBytes>(py)?;
115 let p2: &[u8] = p2.data(py);
116
117 let dirstate_vec: Result<DirstateVec, PyErr> = dmap
118 .items(py)
119 .iter()
120 .map(|(filename, stats)| {
121 let stats = stats.extract::<PySequence>(py)?;
122 let state = stats.get_item(py, 0)?.extract::<PyBytes>(py)?;
123 let state = state.data(py)[0] as i8;
124 let mode = stats.get_item(py, 1)?.extract(py)?;
125 let size = stats.get_item(py, 2)?.extract(py)?;
126 let mtime = stats.get_item(py, 3)?.extract(py)?;
127 let filename = filename.extract::<PyBytes>(py)?;
128 let filename = filename.data(py);
129 Ok((
130 filename.to_owned(),
131 DirstateEntry {
132 state,
133 mode,
134 size,
135 mtime,
136 },
137 ))
138 })
139 .collect();
140
141 let copies: Result<HashMap<Vec<u8>, Vec<u8>>, PyErr> = copymap
142 .items(py)
143 .iter()
144 .map(|(key, value)| {
145 Ok((
146 key.extract::<PyBytes>(py)?.data(py).to_owned(),
147 value.extract::<PyBytes>(py)?.data(py).to_owned(),
148 ))
149 })
150 .collect();
151
152 match pack_dirstate(
153 &dirstate_vec?,
154 &copies?,
155 DirstateParents { p1, p2 },
156 now.value(py) as i32,
157 ) {
158 Ok((packed, new_dirstate_vec)) => {
159 for (
160 filename,
161 DirstateEntry {
162 state,
163 mode,
164 size,
165 mtime,
166 },
167 ) in new_dirstate_vec
168 {
169 dmap.set_item(
170 py,
171 PyBytes::new(py, &filename[..]),
172 decapsule_make_dirstate_tuple(py)?(
173 state, mode, size, mtime,
174 ),
175 )?;
176 }
177 Ok(PyBytes::new(py, &packed))
178 }
179 Err(error) => Err(PyErr::new::<exc::ValueError, _>(
180 py,
181 match error {
182 DirstatePackError::CorruptedParent => {
183 "expected a 20-byte hash".to_string()
184 }
185 DirstatePackError::CorruptedEntry(e) => e,
186 DirstatePackError::BadSize(expected, actual) => {
187 format!("bad dirstate size: {} != {}", actual, expected)
188 }
189 },
190 )),
191 }
192 }
193
194 /// Create the module, with `__package__` given from parent
195 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
196 let dotted_name = &format!("{}.dirstate", package);
197 let m = PyModule::new(py, dotted_name)?;
198 m.add(py, "__package__", package)?;
199 m.add(py, "__doc__", "Dirstate - Rust implementation")?;
200 m.add(
201 py,
202 "parse_dirstate",
203 py_fn!(
204 py,
205 parse_dirstate_wrapper(dmap: PyDict, copymap: PyDict, st: PyBytes)
206 ),
207 )?;
208 m.add(
209 py,
210 "pack_dirstate",
211 py_fn!(
212 py,
213 pack_dirstate_wrapper(
214 dmap: PyDict,
215 copymap: PyDict,
216 pl: PyTuple,
217 now: PyInt
218 )
219 ),
220 )?;
221
222 let sys = PyModule::import(py, "sys")?;
223 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
224 sys_modules.set_item(py, dotted_name, &m)?;
225
226 Ok(m)
227 }
@@ -1,755 +1,762 b''
1 1 /*
2 2 parsers.c - efficient content parsing
3 3
4 4 Copyright 2008 Matt Mackall <mpm@selenic.com> and others
5 5
6 6 This software may be used and distributed according to the terms of
7 7 the GNU General Public License, incorporated herein by reference.
8 8 */
9 9
10 10 #define PY_SSIZE_T_CLEAN
11 11 #include <Python.h>
12 12 #include <ctype.h>
13 13 #include <stddef.h>
14 14 #include <string.h>
15 15
16 16 #include "bitmanipulation.h"
17 17 #include "charencode.h"
18 18 #include "util.h"
19 19
20 20 #ifdef IS_PY3K
21 21 /* The mapping of Python types is meant to be temporary to get Python
22 22 * 3 to compile. We should remove this once Python 3 support is fully
23 23 * supported and proper types are used in the extensions themselves. */
24 24 #define PyInt_Check PyLong_Check
25 25 #define PyInt_FromLong PyLong_FromLong
26 26 #define PyInt_FromSsize_t PyLong_FromSsize_t
27 27 #define PyInt_AsLong PyLong_AsLong
28 28 #endif
29 29
30 30 static const char *const versionerrortext = "Python minor version mismatch";
31 31
32 32 static PyObject *dict_new_presized(PyObject *self, PyObject *args)
33 33 {
34 34 Py_ssize_t expected_size;
35 35
36 36 if (!PyArg_ParseTuple(args, "n:make_presized_dict", &expected_size)) {
37 37 return NULL;
38 38 }
39 39
40 40 return _dict_new_presized(expected_size);
41 41 }
42 42
43 43 static inline dirstateTupleObject *make_dirstate_tuple(char state, int mode,
44 44 int size, int mtime)
45 45 {
46 46 dirstateTupleObject *t =
47 47 PyObject_New(dirstateTupleObject, &dirstateTupleType);
48 48 if (!t) {
49 49 return NULL;
50 50 }
51 51 t->state = state;
52 52 t->mode = mode;
53 53 t->size = size;
54 54 t->mtime = mtime;
55 55 return t;
56 56 }
57 57
58 58 static PyObject *dirstate_tuple_new(PyTypeObject *subtype, PyObject *args,
59 59 PyObject *kwds)
60 60 {
61 61 /* We do all the initialization here and not a tp_init function because
62 62 * dirstate_tuple is immutable. */
63 63 dirstateTupleObject *t;
64 64 char state;
65 65 int size, mode, mtime;
66 66 if (!PyArg_ParseTuple(args, "ciii", &state, &mode, &size, &mtime)) {
67 67 return NULL;
68 68 }
69 69
70 70 t = (dirstateTupleObject *)subtype->tp_alloc(subtype, 1);
71 71 if (!t) {
72 72 return NULL;
73 73 }
74 74 t->state = state;
75 75 t->mode = mode;
76 76 t->size = size;
77 77 t->mtime = mtime;
78 78
79 79 return (PyObject *)t;
80 80 }
81 81
82 82 static void dirstate_tuple_dealloc(PyObject *o)
83 83 {
84 84 PyObject_Del(o);
85 85 }
86 86
87 87 static Py_ssize_t dirstate_tuple_length(PyObject *o)
88 88 {
89 89 return 4;
90 90 }
91 91
92 92 static PyObject *dirstate_tuple_item(PyObject *o, Py_ssize_t i)
93 93 {
94 94 dirstateTupleObject *t = (dirstateTupleObject *)o;
95 95 switch (i) {
96 96 case 0:
97 97 return PyBytes_FromStringAndSize(&t->state, 1);
98 98 case 1:
99 99 return PyInt_FromLong(t->mode);
100 100 case 2:
101 101 return PyInt_FromLong(t->size);
102 102 case 3:
103 103 return PyInt_FromLong(t->mtime);
104 104 default:
105 105 PyErr_SetString(PyExc_IndexError, "index out of range");
106 106 return NULL;
107 107 }
108 108 }
109 109
110 110 static PySequenceMethods dirstate_tuple_sq = {
111 111 dirstate_tuple_length, /* sq_length */
112 112 0, /* sq_concat */
113 113 0, /* sq_repeat */
114 114 dirstate_tuple_item, /* sq_item */
115 115 0, /* sq_ass_item */
116 116 0, /* sq_contains */
117 117 0, /* sq_inplace_concat */
118 118 0 /* sq_inplace_repeat */
119 119 };
120 120
121 121 PyTypeObject dirstateTupleType = {
122 122 PyVarObject_HEAD_INIT(NULL, 0) /* header */
123 123 "dirstate_tuple", /* tp_name */
124 124 sizeof(dirstateTupleObject), /* tp_basicsize */
125 125 0, /* tp_itemsize */
126 126 (destructor)dirstate_tuple_dealloc, /* tp_dealloc */
127 127 0, /* tp_print */
128 128 0, /* tp_getattr */
129 129 0, /* tp_setattr */
130 130 0, /* tp_compare */
131 131 0, /* tp_repr */
132 132 0, /* tp_as_number */
133 133 &dirstate_tuple_sq, /* tp_as_sequence */
134 134 0, /* tp_as_mapping */
135 135 0, /* tp_hash */
136 136 0, /* tp_call */
137 137 0, /* tp_str */
138 138 0, /* tp_getattro */
139 139 0, /* tp_setattro */
140 140 0, /* tp_as_buffer */
141 141 Py_TPFLAGS_DEFAULT, /* tp_flags */
142 142 "dirstate tuple", /* tp_doc */
143 143 0, /* tp_traverse */
144 144 0, /* tp_clear */
145 145 0, /* tp_richcompare */
146 146 0, /* tp_weaklistoffset */
147 147 0, /* tp_iter */
148 148 0, /* tp_iternext */
149 149 0, /* tp_methods */
150 150 0, /* tp_members */
151 151 0, /* tp_getset */
152 152 0, /* tp_base */
153 153 0, /* tp_dict */
154 154 0, /* tp_descr_get */
155 155 0, /* tp_descr_set */
156 156 0, /* tp_dictoffset */
157 157 0, /* tp_init */
158 158 0, /* tp_alloc */
159 159 dirstate_tuple_new, /* tp_new */
160 160 };
161 161
162 162 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
163 163 {
164 164 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
165 165 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
166 166 char state, *cur, *str, *cpos;
167 167 int mode, size, mtime;
168 168 unsigned int flen, pos = 40;
169 169 Py_ssize_t len = 40;
170 170 Py_ssize_t readlen;
171 171
172 172 if (!PyArg_ParseTuple(
173 173 args, PY23("O!O!s#:parse_dirstate", "O!O!y#:parse_dirstate"),
174 174 &PyDict_Type, &dmap, &PyDict_Type, &cmap, &str, &readlen)) {
175 175 goto quit;
176 176 }
177 177
178 178 len = readlen;
179 179
180 180 /* read parents */
181 181 if (len < 40) {
182 182 PyErr_SetString(PyExc_ValueError,
183 183 "too little data for parents");
184 184 goto quit;
185 185 }
186 186
187 187 parents = Py_BuildValue(PY23("s#s#", "y#y#"), str, (Py_ssize_t)20,
188 188 str + 20, (Py_ssize_t)20);
189 189 if (!parents) {
190 190 goto quit;
191 191 }
192 192
193 193 /* read filenames */
194 194 while (pos >= 40 && pos < len) {
195 195 if (pos + 17 > len) {
196 196 PyErr_SetString(PyExc_ValueError,
197 197 "overflow in dirstate");
198 198 goto quit;
199 199 }
200 200 cur = str + pos;
201 201 /* unpack header */
202 202 state = *cur;
203 203 mode = getbe32(cur + 1);
204 204 size = getbe32(cur + 5);
205 205 mtime = getbe32(cur + 9);
206 206 flen = getbe32(cur + 13);
207 207 pos += 17;
208 208 cur += 17;
209 209 if (flen > len - pos) {
210 210 PyErr_SetString(PyExc_ValueError,
211 211 "overflow in dirstate");
212 212 goto quit;
213 213 }
214 214
215 215 entry =
216 216 (PyObject *)make_dirstate_tuple(state, mode, size, mtime);
217 217 cpos = memchr(cur, 0, flen);
218 218 if (cpos) {
219 219 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
220 220 cname = PyBytes_FromStringAndSize(
221 221 cpos + 1, flen - (cpos - cur) - 1);
222 222 if (!fname || !cname ||
223 223 PyDict_SetItem(cmap, fname, cname) == -1 ||
224 224 PyDict_SetItem(dmap, fname, entry) == -1) {
225 225 goto quit;
226 226 }
227 227 Py_DECREF(cname);
228 228 } else {
229 229 fname = PyBytes_FromStringAndSize(cur, flen);
230 230 if (!fname ||
231 231 PyDict_SetItem(dmap, fname, entry) == -1) {
232 232 goto quit;
233 233 }
234 234 }
235 235 Py_DECREF(fname);
236 236 Py_DECREF(entry);
237 237 fname = cname = entry = NULL;
238 238 pos += flen;
239 239 }
240 240
241 241 ret = parents;
242 242 Py_INCREF(ret);
243 243 quit:
244 244 Py_XDECREF(fname);
245 245 Py_XDECREF(cname);
246 246 Py_XDECREF(entry);
247 247 Py_XDECREF(parents);
248 248 return ret;
249 249 }
250 250
251 251 /*
252 252 * Build a set of non-normal and other parent entries from the dirstate dmap
253 253 */
254 254 static PyObject *nonnormalotherparententries(PyObject *self, PyObject *args)
255 255 {
256 256 PyObject *dmap, *fname, *v;
257 257 PyObject *nonnset = NULL, *otherpset = NULL, *result = NULL;
258 258 Py_ssize_t pos;
259 259
260 260 if (!PyArg_ParseTuple(args, "O!:nonnormalentries", &PyDict_Type,
261 261 &dmap)) {
262 262 goto bail;
263 263 }
264 264
265 265 nonnset = PySet_New(NULL);
266 266 if (nonnset == NULL) {
267 267 goto bail;
268 268 }
269 269
270 270 otherpset = PySet_New(NULL);
271 271 if (otherpset == NULL) {
272 272 goto bail;
273 273 }
274 274
275 275 pos = 0;
276 276 while (PyDict_Next(dmap, &pos, &fname, &v)) {
277 277 dirstateTupleObject *t;
278 278 if (!dirstate_tuple_check(v)) {
279 279 PyErr_SetString(PyExc_TypeError,
280 280 "expected a dirstate tuple");
281 281 goto bail;
282 282 }
283 283 t = (dirstateTupleObject *)v;
284 284
285 285 if (t->state == 'n' && t->size == -2) {
286 286 if (PySet_Add(otherpset, fname) == -1) {
287 287 goto bail;
288 288 }
289 289 }
290 290
291 291 if (t->state == 'n' && t->mtime != -1) {
292 292 continue;
293 293 }
294 294 if (PySet_Add(nonnset, fname) == -1) {
295 295 goto bail;
296 296 }
297 297 }
298 298
299 299 result = Py_BuildValue("(OO)", nonnset, otherpset);
300 300 if (result == NULL) {
301 301 goto bail;
302 302 }
303 303 Py_DECREF(nonnset);
304 304 Py_DECREF(otherpset);
305 305 return result;
306 306 bail:
307 307 Py_XDECREF(nonnset);
308 308 Py_XDECREF(otherpset);
309 309 Py_XDECREF(result);
310 310 return NULL;
311 311 }
312 312
313 313 /*
314 314 * Efficiently pack a dirstate object into its on-disk format.
315 315 */
316 316 static PyObject *pack_dirstate(PyObject *self, PyObject *args)
317 317 {
318 318 PyObject *packobj = NULL;
319 319 PyObject *map, *copymap, *pl, *mtime_unset = NULL;
320 320 Py_ssize_t nbytes, pos, l;
321 321 PyObject *k, *v = NULL, *pn;
322 322 char *p, *s;
323 323 int now;
324 324
325 325 if (!PyArg_ParseTuple(args, "O!O!O!i:pack_dirstate", &PyDict_Type, &map,
326 326 &PyDict_Type, &copymap, &PyTuple_Type, &pl,
327 327 &now)) {
328 328 return NULL;
329 329 }
330 330
331 331 if (PyTuple_Size(pl) != 2) {
332 332 PyErr_SetString(PyExc_TypeError, "expected 2-element tuple");
333 333 return NULL;
334 334 }
335 335
336 336 /* Figure out how much we need to allocate. */
337 337 for (nbytes = 40, pos = 0; PyDict_Next(map, &pos, &k, &v);) {
338 338 PyObject *c;
339 339 if (!PyBytes_Check(k)) {
340 340 PyErr_SetString(PyExc_TypeError, "expected string key");
341 341 goto bail;
342 342 }
343 343 nbytes += PyBytes_GET_SIZE(k) + 17;
344 344 c = PyDict_GetItem(copymap, k);
345 345 if (c) {
346 346 if (!PyBytes_Check(c)) {
347 347 PyErr_SetString(PyExc_TypeError,
348 348 "expected string key");
349 349 goto bail;
350 350 }
351 351 nbytes += PyBytes_GET_SIZE(c) + 1;
352 352 }
353 353 }
354 354
355 355 packobj = PyBytes_FromStringAndSize(NULL, nbytes);
356 356 if (packobj == NULL) {
357 357 goto bail;
358 358 }
359 359
360 360 p = PyBytes_AS_STRING(packobj);
361 361
362 362 pn = PyTuple_GET_ITEM(pl, 0);
363 363 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
364 364 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
365 365 goto bail;
366 366 }
367 367 memcpy(p, s, l);
368 368 p += 20;
369 369 pn = PyTuple_GET_ITEM(pl, 1);
370 370 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
371 371 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
372 372 goto bail;
373 373 }
374 374 memcpy(p, s, l);
375 375 p += 20;
376 376
377 377 for (pos = 0; PyDict_Next(map, &pos, &k, &v);) {
378 378 dirstateTupleObject *tuple;
379 379 char state;
380 380 int mode, size, mtime;
381 381 Py_ssize_t len, l;
382 382 PyObject *o;
383 383 char *t;
384 384
385 385 if (!dirstate_tuple_check(v)) {
386 386 PyErr_SetString(PyExc_TypeError,
387 387 "expected a dirstate tuple");
388 388 goto bail;
389 389 }
390 390 tuple = (dirstateTupleObject *)v;
391 391
392 392 state = tuple->state;
393 393 mode = tuple->mode;
394 394 size = tuple->size;
395 395 mtime = tuple->mtime;
396 396 if (state == 'n' && mtime == now) {
397 397 /* See pure/parsers.py:pack_dirstate for why we do
398 398 * this. */
399 399 mtime = -1;
400 400 mtime_unset = (PyObject *)make_dirstate_tuple(
401 401 state, mode, size, mtime);
402 402 if (!mtime_unset) {
403 403 goto bail;
404 404 }
405 405 if (PyDict_SetItem(map, k, mtime_unset) == -1) {
406 406 goto bail;
407 407 }
408 408 Py_DECREF(mtime_unset);
409 409 mtime_unset = NULL;
410 410 }
411 411 *p++ = state;
412 412 putbe32((uint32_t)mode, p);
413 413 putbe32((uint32_t)size, p + 4);
414 414 putbe32((uint32_t)mtime, p + 8);
415 415 t = p + 12;
416 416 p += 16;
417 417 len = PyBytes_GET_SIZE(k);
418 418 memcpy(p, PyBytes_AS_STRING(k), len);
419 419 p += len;
420 420 o = PyDict_GetItem(copymap, k);
421 421 if (o) {
422 422 *p++ = '\0';
423 423 l = PyBytes_GET_SIZE(o);
424 424 memcpy(p, PyBytes_AS_STRING(o), l);
425 425 p += l;
426 426 len += l + 1;
427 427 }
428 428 putbe32((uint32_t)len, t);
429 429 }
430 430
431 431 pos = p - PyBytes_AS_STRING(packobj);
432 432 if (pos != nbytes) {
433 433 PyErr_Format(PyExc_SystemError, "bad dirstate size: %ld != %ld",
434 434 (long)pos, (long)nbytes);
435 435 goto bail;
436 436 }
437 437
438 438 return packobj;
439 439 bail:
440 440 Py_XDECREF(mtime_unset);
441 441 Py_XDECREF(packobj);
442 442 Py_XDECREF(v);
443 443 return NULL;
444 444 }
445 445
446 446 #define BUMPED_FIX 1
447 447 #define USING_SHA_256 2
448 448 #define FM1_HEADER_SIZE (4 + 8 + 2 + 2 + 1 + 1 + 1)
449 449
450 450 static PyObject *readshas(const char *source, unsigned char num,
451 451 Py_ssize_t hashwidth)
452 452 {
453 453 int i;
454 454 PyObject *list = PyTuple_New(num);
455 455 if (list == NULL) {
456 456 return NULL;
457 457 }
458 458 for (i = 0; i < num; i++) {
459 459 PyObject *hash = PyBytes_FromStringAndSize(source, hashwidth);
460 460 if (hash == NULL) {
461 461 Py_DECREF(list);
462 462 return NULL;
463 463 }
464 464 PyTuple_SET_ITEM(list, i, hash);
465 465 source += hashwidth;
466 466 }
467 467 return list;
468 468 }
469 469
470 470 static PyObject *fm1readmarker(const char *databegin, const char *dataend,
471 471 uint32_t *msize)
472 472 {
473 473 const char *data = databegin;
474 474 const char *meta;
475 475
476 476 double mtime;
477 477 int16_t tz;
478 478 uint16_t flags;
479 479 unsigned char nsuccs, nparents, nmetadata;
480 480 Py_ssize_t hashwidth = 20;
481 481
482 482 PyObject *prec = NULL, *parents = NULL, *succs = NULL;
483 483 PyObject *metadata = NULL, *ret = NULL;
484 484 int i;
485 485
486 486 if (data + FM1_HEADER_SIZE > dataend) {
487 487 goto overflow;
488 488 }
489 489
490 490 *msize = getbe32(data);
491 491 data += 4;
492 492 mtime = getbefloat64(data);
493 493 data += 8;
494 494 tz = getbeint16(data);
495 495 data += 2;
496 496 flags = getbeuint16(data);
497 497 data += 2;
498 498
499 499 if (flags & USING_SHA_256) {
500 500 hashwidth = 32;
501 501 }
502 502
503 503 nsuccs = (unsigned char)(*data++);
504 504 nparents = (unsigned char)(*data++);
505 505 nmetadata = (unsigned char)(*data++);
506 506
507 507 if (databegin + *msize > dataend) {
508 508 goto overflow;
509 509 }
510 510 dataend = databegin + *msize; /* narrow down to marker size */
511 511
512 512 if (data + hashwidth > dataend) {
513 513 goto overflow;
514 514 }
515 515 prec = PyBytes_FromStringAndSize(data, hashwidth);
516 516 data += hashwidth;
517 517 if (prec == NULL) {
518 518 goto bail;
519 519 }
520 520
521 521 if (data + nsuccs * hashwidth > dataend) {
522 522 goto overflow;
523 523 }
524 524 succs = readshas(data, nsuccs, hashwidth);
525 525 if (succs == NULL) {
526 526 goto bail;
527 527 }
528 528 data += nsuccs * hashwidth;
529 529
530 530 if (nparents == 1 || nparents == 2) {
531 531 if (data + nparents * hashwidth > dataend) {
532 532 goto overflow;
533 533 }
534 534 parents = readshas(data, nparents, hashwidth);
535 535 if (parents == NULL) {
536 536 goto bail;
537 537 }
538 538 data += nparents * hashwidth;
539 539 } else {
540 540 parents = Py_None;
541 541 Py_INCREF(parents);
542 542 }
543 543
544 544 if (data + 2 * nmetadata > dataend) {
545 545 goto overflow;
546 546 }
547 547 meta = data + (2 * nmetadata);
548 548 metadata = PyTuple_New(nmetadata);
549 549 if (metadata == NULL) {
550 550 goto bail;
551 551 }
552 552 for (i = 0; i < nmetadata; i++) {
553 553 PyObject *tmp, *left = NULL, *right = NULL;
554 554 Py_ssize_t leftsize = (unsigned char)(*data++);
555 555 Py_ssize_t rightsize = (unsigned char)(*data++);
556 556 if (meta + leftsize + rightsize > dataend) {
557 557 goto overflow;
558 558 }
559 559 left = PyBytes_FromStringAndSize(meta, leftsize);
560 560 meta += leftsize;
561 561 right = PyBytes_FromStringAndSize(meta, rightsize);
562 562 meta += rightsize;
563 563 tmp = PyTuple_New(2);
564 564 if (!left || !right || !tmp) {
565 565 Py_XDECREF(left);
566 566 Py_XDECREF(right);
567 567 Py_XDECREF(tmp);
568 568 goto bail;
569 569 }
570 570 PyTuple_SET_ITEM(tmp, 0, left);
571 571 PyTuple_SET_ITEM(tmp, 1, right);
572 572 PyTuple_SET_ITEM(metadata, i, tmp);
573 573 }
574 574 ret = Py_BuildValue("(OOHO(di)O)", prec, succs, flags, metadata, mtime,
575 575 (int)tz * 60, parents);
576 576 goto bail; /* return successfully */
577 577
578 578 overflow:
579 579 PyErr_SetString(PyExc_ValueError, "overflow in obsstore");
580 580 bail:
581 581 Py_XDECREF(prec);
582 582 Py_XDECREF(succs);
583 583 Py_XDECREF(metadata);
584 584 Py_XDECREF(parents);
585 585 return ret;
586 586 }
587 587
588 588 static PyObject *fm1readmarkers(PyObject *self, PyObject *args)
589 589 {
590 590 const char *data, *dataend;
591 591 Py_ssize_t datalen, offset, stop;
592 592 PyObject *markers = NULL;
593 593
594 594 if (!PyArg_ParseTuple(args, PY23("s#nn", "y#nn"), &data, &datalen,
595 595 &offset, &stop)) {
596 596 return NULL;
597 597 }
598 598 if (offset < 0) {
599 599 PyErr_SetString(PyExc_ValueError,
600 600 "invalid negative offset in fm1readmarkers");
601 601 return NULL;
602 602 }
603 603 if (stop > datalen) {
604 604 PyErr_SetString(
605 605 PyExc_ValueError,
606 606 "stop longer than data length in fm1readmarkers");
607 607 return NULL;
608 608 }
609 609 dataend = data + datalen;
610 610 data += offset;
611 611 markers = PyList_New(0);
612 612 if (!markers) {
613 613 return NULL;
614 614 }
615 615 while (offset < stop) {
616 616 uint32_t msize;
617 617 int error;
618 618 PyObject *record = fm1readmarker(data, dataend, &msize);
619 619 if (!record) {
620 620 goto bail;
621 621 }
622 622 error = PyList_Append(markers, record);
623 623 Py_DECREF(record);
624 624 if (error) {
625 625 goto bail;
626 626 }
627 627 data += msize;
628 628 offset += msize;
629 629 }
630 630 return markers;
631 631 bail:
632 632 Py_DECREF(markers);
633 633 return NULL;
634 634 }
635 635
636 636 static char parsers_doc[] = "Efficient content parsing.";
637 637
638 638 PyObject *encodedir(PyObject *self, PyObject *args);
639 639 PyObject *pathencode(PyObject *self, PyObject *args);
640 640 PyObject *lowerencode(PyObject *self, PyObject *args);
641 641 PyObject *parse_index2(PyObject *self, PyObject *args);
642 642
643 643 static PyMethodDef methods[] = {
644 644 {"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"},
645 645 {"nonnormalotherparententries", nonnormalotherparententries, METH_VARARGS,
646 646 "create a set containing non-normal and other parent entries of given "
647 647 "dirstate\n"},
648 648 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
649 649 {"parse_index2", parse_index2, METH_VARARGS, "parse a revlog index\n"},
650 650 {"isasciistr", isasciistr, METH_VARARGS, "check if an ASCII string\n"},
651 651 {"asciilower", asciilower, METH_VARARGS, "lowercase an ASCII string\n"},
652 652 {"asciiupper", asciiupper, METH_VARARGS, "uppercase an ASCII string\n"},
653 653 {"dict_new_presized", dict_new_presized, METH_VARARGS,
654 654 "construct a dict with an expected size\n"},
655 655 {"make_file_foldmap", make_file_foldmap, METH_VARARGS,
656 656 "make file foldmap\n"},
657 657 {"jsonescapeu8fast", jsonescapeu8fast, METH_VARARGS,
658 658 "escape a UTF-8 byte string to JSON (fast path)\n"},
659 659 {"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"},
660 660 {"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"},
661 661 {"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"},
662 662 {"fm1readmarkers", fm1readmarkers, METH_VARARGS,
663 663 "parse v1 obsolete markers\n"},
664 664 {NULL, NULL}};
665 665
666 666 void dirs_module_init(PyObject *mod);
667 667 void manifest_module_init(PyObject *mod);
668 668 void revlog_module_init(PyObject *mod);
669 669
670 670 static const int version = 12;
671 671
672 672 static void module_init(PyObject *mod)
673 673 {
674 PyObject *capsule = NULL;
674 675 PyModule_AddIntConstant(mod, "version", version);
675 676
676 677 /* This module constant has two purposes. First, it lets us unit test
677 678 * the ImportError raised without hard-coding any error text. This
678 679 * means we can change the text in the future without breaking tests,
679 680 * even across changesets without a recompile. Second, its presence
680 681 * can be used to determine whether the version-checking logic is
681 682 * present, which also helps in testing across changesets without a
682 683 * recompile. Note that this means the pure-Python version of parsers
683 684 * should not have this module constant. */
684 685 PyModule_AddStringConstant(mod, "versionerrortext", versionerrortext);
685 686
686 687 dirs_module_init(mod);
687 688 manifest_module_init(mod);
688 689 revlog_module_init(mod);
689 690
691 capsule = PyCapsule_New(
692 make_dirstate_tuple,
693 "mercurial.cext.parsers.make_dirstate_tuple_CAPI", NULL);
694 if (capsule != NULL)
695 PyModule_AddObject(mod, "make_dirstate_tuple_CAPI", capsule);
696
690 697 if (PyType_Ready(&dirstateTupleType) < 0) {
691 698 return;
692 699 }
693 700 Py_INCREF(&dirstateTupleType);
694 701 PyModule_AddObject(mod, "dirstatetuple",
695 702 (PyObject *)&dirstateTupleType);
696 703 }
697 704
698 705 static int check_python_version(void)
699 706 {
700 707 PyObject *sys = PyImport_ImportModule("sys"), *ver;
701 708 long hexversion;
702 709 if (!sys) {
703 710 return -1;
704 711 }
705 712 ver = PyObject_GetAttrString(sys, "hexversion");
706 713 Py_DECREF(sys);
707 714 if (!ver) {
708 715 return -1;
709 716 }
710 717 hexversion = PyInt_AsLong(ver);
711 718 Py_DECREF(ver);
712 719 /* sys.hexversion is a 32-bit number by default, so the -1 case
713 720 * should only occur in unusual circumstances (e.g. if sys.hexversion
714 721 * is manually set to an invalid value). */
715 722 if ((hexversion == -1) || (hexversion >> 16 != PY_VERSION_HEX >> 16)) {
716 723 PyErr_Format(PyExc_ImportError,
717 724 "%s: The Mercurial extension "
718 725 "modules were compiled with Python " PY_VERSION
719 726 ", but "
720 727 "Mercurial is currently using Python with "
721 728 "sys.hexversion=%ld: "
722 729 "Python %s\n at: %s",
723 730 versionerrortext, hexversion, Py_GetVersion(),
724 731 Py_GetProgramFullPath());
725 732 return -1;
726 733 }
727 734 return 0;
728 735 }
729 736
730 737 #ifdef IS_PY3K
731 738 static struct PyModuleDef parsers_module = {PyModuleDef_HEAD_INIT, "parsers",
732 739 parsers_doc, -1, methods};
733 740
734 741 PyMODINIT_FUNC PyInit_parsers(void)
735 742 {
736 743 PyObject *mod;
737 744
738 745 if (check_python_version() == -1)
739 746 return NULL;
740 747 mod = PyModule_Create(&parsers_module);
741 748 module_init(mod);
742 749 return mod;
743 750 }
744 751 #else
745 752 PyMODINIT_FUNC initparsers(void)
746 753 {
747 754 PyObject *mod;
748 755
749 756 if (check_python_version() == -1) {
750 757 return;
751 758 }
752 759 mod = Py_InitModule3("parsers", methods, parsers_doc);
753 760 module_init(mod);
754 761 }
755 762 #endif
@@ -1,341 +1,332 b''
1 1 [[package]]
2 2 name = "aho-corasick"
3 3 version = "0.6.9"
4 4 source = "registry+https://github.com/rust-lang/crates.io-index"
5 5 dependencies = [
6 "memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
6 "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
7 7 ]
8 8
9 9 [[package]]
10 10 name = "autocfg"
11 11 version = "0.1.2"
12 12 source = "registry+https://github.com/rust-lang/crates.io-index"
13 13
14 14 [[package]]
15 15 name = "bitflags"
16 16 version = "1.0.4"
17 17 source = "registry+https://github.com/rust-lang/crates.io-index"
18 18
19 19 [[package]]
20 name = "cfg-if"
21 version = "0.1.6"
20 name = "byteorder"
21 version = "1.3.1"
22 22 source = "registry+https://github.com/rust-lang/crates.io-index"
23 23
24 24 [[package]]
25 25 name = "cloudabi"
26 26 version = "0.0.3"
27 27 source = "registry+https://github.com/rust-lang/crates.io-index"
28 28 dependencies = [
29 29 "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
30 30 ]
31 31
32 32 [[package]]
33 33 name = "cpython"
34 34 version = "0.2.1"
35 35 source = "registry+https://github.com/rust-lang/crates.io-index"
36 36 dependencies = [
37 37 "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
38 38 "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
39 39 "python27-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
40 40 "python3-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
41 41 ]
42 42
43 43 [[package]]
44 44 name = "fuchsia-cprng"
45 45 version = "0.1.0"
46 46 source = "registry+https://github.com/rust-lang/crates.io-index"
47 47
48 48 [[package]]
49 49 name = "hg-core"
50 50 version = "0.1.0"
51 51 dependencies = [
52 "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
53 "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
52 54 "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
53 55 "rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
54 56 ]
55 57
56 58 [[package]]
57 59 name = "hg-cpython"
58 60 version = "0.1.0"
59 61 dependencies = [
60 62 "cpython 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
61 63 "hg-core 0.1.0",
62 64 "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
63 65 "python27-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
64 66 "python3-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
65 67 ]
66 68
67 69 [[package]]
68 70 name = "hgdirectffi"
69 71 version = "0.1.0"
70 72 dependencies = [
71 73 "hg-core 0.1.0",
72 74 "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
73 75 ]
74 76
75 77 [[package]]
76 78 name = "lazy_static"
77 79 version = "1.2.0"
78 80 source = "registry+https://github.com/rust-lang/crates.io-index"
79 81
80 82 [[package]]
81 83 name = "libc"
82 84 version = "0.2.45"
83 85 source = "registry+https://github.com/rust-lang/crates.io-index"
84 86
85 87 [[package]]
86 88 name = "memchr"
87 version = "2.1.2"
89 version = "2.2.0"
88 90 source = "registry+https://github.com/rust-lang/crates.io-index"
89 dependencies = [
90 "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
91 "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
92 "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
93 ]
94 91
95 92 [[package]]
96 93 name = "num-traits"
97 94 version = "0.2.6"
98 95 source = "registry+https://github.com/rust-lang/crates.io-index"
99 96
100 97 [[package]]
101 98 name = "python27-sys"
102 99 version = "0.2.1"
103 100 source = "registry+https://github.com/rust-lang/crates.io-index"
104 101 dependencies = [
105 102 "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
106 103 "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
107 104 ]
108 105
109 106 [[package]]
110 107 name = "python3-sys"
111 108 version = "0.2.1"
112 109 source = "registry+https://github.com/rust-lang/crates.io-index"
113 110 dependencies = [
114 111 "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
115 112 "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
116 113 ]
117 114
118 115 [[package]]
119 116 name = "rand"
120 117 version = "0.6.5"
121 118 source = "registry+https://github.com/rust-lang/crates.io-index"
122 119 dependencies = [
123 120 "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
124 121 "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
125 122 "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
126 123 "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
127 124 "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
128 125 "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
129 126 "rand_jitter 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
130 127 "rand_os 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
131 128 "rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
132 129 "rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
133 130 "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
134 131 ]
135 132
136 133 [[package]]
137 134 name = "rand_chacha"
138 135 version = "0.1.1"
139 136 source = "registry+https://github.com/rust-lang/crates.io-index"
140 137 dependencies = [
141 138 "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
142 139 "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
143 140 ]
144 141
145 142 [[package]]
146 143 name = "rand_core"
147 144 version = "0.3.1"
148 145 source = "registry+https://github.com/rust-lang/crates.io-index"
149 146 dependencies = [
150 147 "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
151 148 ]
152 149
153 150 [[package]]
154 151 name = "rand_core"
155 152 version = "0.4.0"
156 153 source = "registry+https://github.com/rust-lang/crates.io-index"
157 154
158 155 [[package]]
159 156 name = "rand_hc"
160 157 version = "0.1.0"
161 158 source = "registry+https://github.com/rust-lang/crates.io-index"
162 159 dependencies = [
163 160 "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
164 161 ]
165 162
166 163 [[package]]
167 164 name = "rand_isaac"
168 165 version = "0.1.1"
169 166 source = "registry+https://github.com/rust-lang/crates.io-index"
170 167 dependencies = [
171 168 "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
172 169 ]
173 170
174 171 [[package]]
175 172 name = "rand_jitter"
176 173 version = "0.1.2"
177 174 source = "registry+https://github.com/rust-lang/crates.io-index"
178 175 dependencies = [
179 176 "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
180 177 "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
181 178 "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
182 179 ]
183 180
184 181 [[package]]
185 182 name = "rand_os"
186 183 version = "0.1.2"
187 184 source = "registry+https://github.com/rust-lang/crates.io-index"
188 185 dependencies = [
189 186 "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
190 187 "fuchsia-cprng 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
191 188 "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
192 189 "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
193 190 "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
194 191 "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
195 192 ]
196 193
197 194 [[package]]
198 195 name = "rand_pcg"
199 196 version = "0.1.1"
200 197 source = "registry+https://github.com/rust-lang/crates.io-index"
201 198 dependencies = [
202 199 "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
203 200 "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
204 201 ]
205 202
206 203 [[package]]
207 204 name = "rand_xorshift"
208 205 version = "0.1.1"
209 206 source = "registry+https://github.com/rust-lang/crates.io-index"
210 207 dependencies = [
211 208 "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
212 209 ]
213 210
214 211 [[package]]
215 212 name = "rdrand"
216 213 version = "0.4.0"
217 214 source = "registry+https://github.com/rust-lang/crates.io-index"
218 215 dependencies = [
219 216 "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
220 217 ]
221 218
222 219 [[package]]
223 220 name = "regex"
224 221 version = "1.1.0"
225 222 source = "registry+https://github.com/rust-lang/crates.io-index"
226 223 dependencies = [
227 224 "aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
228 "memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
225 "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
229 226 "regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
230 227 "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
231 228 "utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
232 229 ]
233 230
234 231 [[package]]
235 232 name = "regex-syntax"
236 233 version = "0.6.4"
237 234 source = "registry+https://github.com/rust-lang/crates.io-index"
238 235 dependencies = [
239 236 "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
240 237 ]
241 238
242 239 [[package]]
243 240 name = "rustc_version"
244 241 version = "0.2.3"
245 242 source = "registry+https://github.com/rust-lang/crates.io-index"
246 243 dependencies = [
247 244 "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
248 245 ]
249 246
250 247 [[package]]
251 248 name = "semver"
252 249 version = "0.9.0"
253 250 source = "registry+https://github.com/rust-lang/crates.io-index"
254 251 dependencies = [
255 252 "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
256 253 ]
257 254
258 255 [[package]]
259 256 name = "semver-parser"
260 257 version = "0.7.0"
261 258 source = "registry+https://github.com/rust-lang/crates.io-index"
262 259
263 260 [[package]]
264 261 name = "thread_local"
265 262 version = "0.3.6"
266 263 source = "registry+https://github.com/rust-lang/crates.io-index"
267 264 dependencies = [
268 265 "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
269 266 ]
270 267
271 268 [[package]]
272 269 name = "ucd-util"
273 270 version = "0.1.3"
274 271 source = "registry+https://github.com/rust-lang/crates.io-index"
275 272
276 273 [[package]]
277 274 name = "utf8-ranges"
278 275 version = "1.0.2"
279 276 source = "registry+https://github.com/rust-lang/crates.io-index"
280 277
281 278 [[package]]
282 name = "version_check"
283 version = "0.1.5"
284 source = "registry+https://github.com/rust-lang/crates.io-index"
285
286 [[package]]
287 279 name = "winapi"
288 280 version = "0.3.6"
289 281 source = "registry+https://github.com/rust-lang/crates.io-index"
290 282 dependencies = [
291 283 "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
292 284 "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
293 285 ]
294 286
295 287 [[package]]
296 288 name = "winapi-i686-pc-windows-gnu"
297 289 version = "0.4.0"
298 290 source = "registry+https://github.com/rust-lang/crates.io-index"
299 291
300 292 [[package]]
301 293 name = "winapi-x86_64-pc-windows-gnu"
302 294 version = "0.4.0"
303 295 source = "registry+https://github.com/rust-lang/crates.io-index"
304 296
305 297 [metadata]
306 298 "checksum aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9a933f4e58658d7b12defcf96dc5c720f20832deebe3e0a19efd3b6aaeeb9e"
307 299 "checksum autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799"
308 300 "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
309 "checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
301 "checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb"
310 302 "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
311 303 "checksum cpython 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b489034e723e7f5109fecd19b719e664f89ef925be785885252469e9822fa940"
312 304 "checksum fuchsia-cprng 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "81f7f8eb465745ea9b02e2704612a9946a59fa40572086c6fd49d6ddcf30bf31"
313 305 "checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1"
314 306 "checksum libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "2d2857ec59fadc0773853c664d2d18e7198e83883e7060b63c924cb077bd5c74"
315 "checksum memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "db4c41318937f6e76648f42826b1d9ade5c09cafb5aef7e351240a70f39206e9"
307 "checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39"
316 308 "checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1"
317 309 "checksum python27-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "56114c37d4dca82526d74009df7782a28c871ac9d36b19d4cb9e67672258527e"
318 310 "checksum python3-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "61e4aac43f833fd637e429506cb2ac9d7df672c4b68f2eaaa163649b7fdc0444"
319 311 "checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
320 312 "checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
321 313 "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
322 314 "checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0"
323 315 "checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4"
324 316 "checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08"
325 317 "checksum rand_jitter 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "080723c6145e37503a2224f801f252e14ac5531cb450f4502698542d188cb3c0"
326 318 "checksum rand_os 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b7c690732391ae0abafced5015ffb53656abfaec61b342290e5eb56b286a679d"
327 319 "checksum rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "086bd09a33c7044e56bb44d5bdde5a60e7f119a9e95b0775f545de759a32fe05"
328 320 "checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c"
329 321 "checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
330 322 "checksum regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37e7cbbd370869ce2e8dff25c7018702d10b21a20ef7135316f8daecd6c25b7f"
331 323 "checksum regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4e47a2ed29da7a9e1960e1639e7a982e6edc6d49be308a3b02daf511504a16d1"
332 324 "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
333 325 "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
334 326 "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
335 327 "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
336 328 "checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86"
337 329 "checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737"
338 "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
339 330 "checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"
340 331 "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
341 332 "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
@@ -1,71 +1,75 b''
1 1 // Copyright 2018 Georges Racinet <gracinet@anybox.fr>
2 2 //
3 3 // This software may be used and distributed according to the terms of the
4 4 // GNU General Public License version 2 or any later version.
5 5 extern crate byteorder;
6 6 extern crate memchr;
7 7
8 8 mod ancestors;
9 9 pub mod dagops;
10 10 pub use ancestors::{AncestorsIterator, LazyAncestors, MissingAncestors};
11 mod dirstate;
12 pub mod discovery;
11 13 pub mod testing; // unconditionally built, for use from integration tests
12 pub mod discovery;
14 pub use dirstate::{
15 pack_dirstate, parse_dirstate, CopyVec, CopyVecEntry, DirstateEntry,
16 DirstateParents, DirstateVec,
17 };
13 18
14 19 /// Mercurial revision numbers
15 20 ///
16 21 /// As noted in revlog.c, revision numbers are actually encoded in
17 22 /// 4 bytes, and are liberally converted to ints, whence the i32
18 23 pub type Revision = i32;
19 24
20
21 25 /// Marker expressing the absence of a parent
22 26 ///
23 27 /// Independently of the actual representation, `NULL_REVISION` is guaranteed
24 28 /// to be smaller that all existing revisions.
25 29 pub const NULL_REVISION: Revision = -1;
26 30
27 31 /// Same as `mercurial.node.wdirrev`
28 32 ///
29 33 /// This is also equal to `i32::max_value()`, but it's better to spell
30 34 /// it out explicitely, same as in `mercurial.node`
31 35 pub const WORKING_DIRECTORY_REVISION: Revision = 0x7fffffff;
32 36
33 37 /// The simplest expression of what we need of Mercurial DAGs.
34 38 pub trait Graph {
35 39 /// Return the two parents of the given `Revision`.
36 40 ///
37 41 /// Each of the parents can be independently `NULL_REVISION`
38 42 fn parents(&self, Revision) -> Result<[Revision; 2], GraphError>;
39 43 }
40 44
41 45 #[derive(Clone, Debug, PartialEq)]
42 46 pub enum GraphError {
43 47 ParentOutOfRange(Revision),
44 48 WorkingDirectoryUnsupported,
45 49 }
46 50
47 51 #[derive(Clone, Debug, PartialEq)]
48 52 pub enum DirstateParseError {
49 53 TooLittleData,
50 54 Overflow,
51 55 CorruptedEntry(String),
52 56 }
53 57
54 58 #[derive(Debug, PartialEq)]
55 59 pub enum DirstatePackError {
56 60 CorruptedEntry(String),
57 61 CorruptedParent,
58 62 BadSize(usize, usize),
59 63 }
60 64
61 65 impl From<std::io::Error> for DirstatePackError {
62 66 fn from(e: std::io::Error) -> Self {
63 67 DirstatePackError::CorruptedEntry(e.to_string())
64 68 }
65 69 }
66 70
67 71 impl From<std::io::Error> for DirstateParseError {
68 72 fn from(e: std::io::Error) -> Self {
69 73 DirstateParseError::CorruptedEntry(e.to_string())
70 74 }
71 75 }
@@ -1,47 +1,50 b''
1 1 // lib.rs
2 2 //
3 3 // Copyright 2018 Georges Racinet <gracinet@anybox.fr>
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 //! Python bindings of `hg-core` objects using the `cpython` crate.
9 9 //! Once compiled, the resulting single shared library object can be placed in
10 10 //! the `mercurial` package directly as `rustext.so` or `rustext.dll`.
11 11 //! It holds several modules, so that from the point of view of Python,
12 12 //! it behaves as the `cext` package.
13 13 //!
14 14 //! Example:
15 15 //!
16 16 //! ```text
17 17 //! >>> from mercurial.rustext import ancestor
18 18 //! >>> ancestor.__doc__
19 19 //! 'Generic DAG ancestor algorithms - Rust implementation'
20 20 //! ```
21 21
22 22 #[macro_use]
23 23 extern crate cpython;
24 24 extern crate hg;
25 25 extern crate libc;
26 extern crate python27_sys;
26 27
27 28 pub mod ancestors;
28 29 mod cindex;
29 30 mod conversion;
30 31 pub mod dagops;
31 32 pub mod discovery;
32 33 pub mod exceptions;
34 pub mod dirstate;
33 35
34 36 py_module_initializer!(rustext, initrustext, PyInit_rustext, |py, m| {
35 37 m.add(
36 38 py,
37 39 "__doc__",
38 40 "Mercurial core concepts - Rust implementation",
39 41 )?;
40 42
41 43 let dotted_name: String = m.get(py, "__name__")?.extract(py)?;
42 44 m.add(py, "ancestor", ancestors::init_module(py, &dotted_name)?)?;
43 45 m.add(py, "dagop", dagops::init_module(py, &dotted_name)?)?;
44 46 m.add(py, "discovery", discovery::init_module(py, &dotted_name)?)?;
47 m.add(py, "dirstate", dirstate::init_module(py, &dotted_name)?)?;
45 48 m.add(py, "GraphError", py.get_type::<exceptions::GraphError>())?;
46 49 Ok(())
47 50 });
General Comments 0
You need to be logged in to leave comments. Login now