##// END OF EJS Templates
dirstate-item: add a `from_v1_data` constructor...
marmoute -
r48465:a4443f66 default
parent child Browse files
Show More
@@ -1,913 +1,957
1 1 /*
2 2 parsers.c - efficient content parsing
3 3
4 4 Copyright 2008 Olivia Mackall <olivia@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 const int dirstate_v1_from_p2 = -2;
33 33 static const int dirstate_v1_nonnormal = -1;
34 34
35 35 static PyObject *dict_new_presized(PyObject *self, PyObject *args)
36 36 {
37 37 Py_ssize_t expected_size;
38 38
39 39 if (!PyArg_ParseTuple(args, "n:make_presized_dict", &expected_size)) {
40 40 return NULL;
41 41 }
42 42
43 43 return _dict_new_presized(expected_size);
44 44 }
45 45
46 46 static inline dirstateItemObject *make_dirstate_item(char state, int mode,
47 47 int size, int mtime)
48 48 {
49 49 dirstateItemObject *t =
50 50 PyObject_New(dirstateItemObject, &dirstateItemType);
51 51 if (!t) {
52 52 return NULL;
53 53 }
54 54 t->state = state;
55 55 t->mode = mode;
56 56 t->size = size;
57 57 t->mtime = mtime;
58 58 return t;
59 59 }
60 60
61 61 static PyObject *dirstate_item_new(PyTypeObject *subtype, PyObject *args,
62 62 PyObject *kwds)
63 63 {
64 64 /* We do all the initialization here and not a tp_init function because
65 65 * dirstate_item is immutable. */
66 66 dirstateItemObject *t;
67 67 char state;
68 68 int size, mode, mtime;
69 69 if (!PyArg_ParseTuple(args, "ciii", &state, &mode, &size, &mtime)) {
70 70 return NULL;
71 71 }
72 72
73 73 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
74 74 if (!t) {
75 75 return NULL;
76 76 }
77 77 t->state = state;
78 78 t->mode = mode;
79 79 t->size = size;
80 80 t->mtime = mtime;
81 81
82 82 return (PyObject *)t;
83 83 }
84 84
85 85 static void dirstate_item_dealloc(PyObject *o)
86 86 {
87 87 PyObject_Del(o);
88 88 }
89 89
90 90 static Py_ssize_t dirstate_item_length(PyObject *o)
91 91 {
92 92 return 4;
93 93 }
94 94
95 95 static PyObject *dirstate_item_item(PyObject *o, Py_ssize_t i)
96 96 {
97 97 dirstateItemObject *t = (dirstateItemObject *)o;
98 98 switch (i) {
99 99 case 0:
100 100 return PyBytes_FromStringAndSize(&t->state, 1);
101 101 case 1:
102 102 return PyInt_FromLong(t->mode);
103 103 case 2:
104 104 return PyInt_FromLong(t->size);
105 105 case 3:
106 106 return PyInt_FromLong(t->mtime);
107 107 default:
108 108 PyErr_SetString(PyExc_IndexError, "index out of range");
109 109 return NULL;
110 110 }
111 111 }
112 112
113 113 static PySequenceMethods dirstate_item_sq = {
114 114 dirstate_item_length, /* sq_length */
115 115 0, /* sq_concat */
116 116 0, /* sq_repeat */
117 117 dirstate_item_item, /* sq_item */
118 118 0, /* sq_ass_item */
119 119 0, /* sq_contains */
120 120 0, /* sq_inplace_concat */
121 121 0 /* sq_inplace_repeat */
122 122 };
123 123
124 124 static PyObject *dirstate_item_v1_state(dirstateItemObject *self)
125 125 {
126 126 return PyBytes_FromStringAndSize(&self->state, 1);
127 127 };
128 128
129 129 static PyObject *dirstate_item_v1_mode(dirstateItemObject *self)
130 130 {
131 131 return PyInt_FromLong(self->mode);
132 132 };
133 133
134 134 static PyObject *dirstate_item_v1_size(dirstateItemObject *self)
135 135 {
136 136 return PyInt_FromLong(self->size);
137 137 };
138 138
139 139 static PyObject *dirstate_item_v1_mtime(dirstateItemObject *self)
140 140 {
141 141 return PyInt_FromLong(self->mtime);
142 142 };
143 143
144 144 static PyObject *dirstate_item_need_delay(dirstateItemObject *self,
145 145 PyObject *value)
146 146 {
147 147 long now;
148 148 if (!pylong_to_long(value, &now)) {
149 149 return NULL;
150 150 }
151 151 if (self->state == 'n' && self->mtime == now) {
152 152 Py_RETURN_TRUE;
153 153 } else {
154 154 Py_RETURN_FALSE;
155 155 }
156 156 };
157 157
158 /* This will never change since it's bound to V1, unlike `make_dirstate_item`
159 */
160 static inline dirstateItemObject *
161 dirstate_item_from_v1_data(char state, int mode, int size, int mtime)
162 {
163 dirstateItemObject *t =
164 PyObject_New(dirstateItemObject, &dirstateItemType);
165 if (!t) {
166 return NULL;
167 }
168 t->state = state;
169 t->mode = mode;
170 t->size = size;
171 t->mtime = mtime;
172 return t;
173 }
174
175 /* This will never change since it's bound to V1, unlike `dirstate_item_new` */
176 static PyObject *dirstate_item_from_v1_meth(PyTypeObject *subtype,
177 PyObject *args)
178 {
179 /* We do all the initialization here and not a tp_init function because
180 * dirstate_item is immutable. */
181 dirstateItemObject *t;
182 char state;
183 int size, mode, mtime;
184 if (!PyArg_ParseTuple(args, "ciii", &state, &mode, &size, &mtime)) {
185 return NULL;
186 }
187
188 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
189 if (!t) {
190 return NULL;
191 }
192 t->state = state;
193 t->mode = mode;
194 t->size = size;
195 t->mtime = mtime;
196
197 return (PyObject *)t;
198 };
199
158 200 static PyMethodDef dirstate_item_methods[] = {
159 201 {"v1_state", (PyCFunction)dirstate_item_v1_state, METH_NOARGS,
160 202 "return a \"state\" suitable for v1 serialization"},
161 203 {"v1_mode", (PyCFunction)dirstate_item_v1_mode, METH_NOARGS,
162 204 "return a \"mode\" suitable for v1 serialization"},
163 205 {"v1_size", (PyCFunction)dirstate_item_v1_size, METH_NOARGS,
164 206 "return a \"size\" suitable for v1 serialization"},
165 207 {"v1_mtime", (PyCFunction)dirstate_item_v1_mtime, METH_NOARGS,
166 208 "return a \"mtime\" suitable for v1 serialization"},
167 209 {"need_delay", (PyCFunction)dirstate_item_need_delay, METH_O,
168 210 "True if the stored mtime would be ambiguous with the current time"},
211 {"from_v1_data", (PyCFunction)dirstate_item_from_v1_meth, METH_O,
212 "build a new DirstateItem object from V1 data"},
169 213 {NULL} /* Sentinel */
170 214 };
171 215
172 216 static PyObject *dirstate_item_get_mode(dirstateItemObject *self)
173 217 {
174 218 return PyInt_FromLong(self->mode);
175 219 };
176 220
177 221 static PyObject *dirstate_item_get_size(dirstateItemObject *self)
178 222 {
179 223 return PyInt_FromLong(self->size);
180 224 };
181 225
182 226 static PyObject *dirstate_item_get_mtime(dirstateItemObject *self)
183 227 {
184 228 return PyInt_FromLong(self->mtime);
185 229 };
186 230
187 231 static PyObject *dirstate_item_get_state(dirstateItemObject *self)
188 232 {
189 233 return PyBytes_FromStringAndSize(&self->state, 1);
190 234 };
191 235
192 236 static PyObject *dirstate_item_get_tracked(dirstateItemObject *self)
193 237 {
194 238 if (self->state == 'a' || self->state == 'm' || self->state == 'n') {
195 239 Py_RETURN_TRUE;
196 240 } else {
197 241 Py_RETURN_FALSE;
198 242 }
199 243 };
200 244
201 245 static PyObject *dirstate_item_get_added(dirstateItemObject *self)
202 246 {
203 247 if (self->state == 'a') {
204 248 Py_RETURN_TRUE;
205 249 } else {
206 250 Py_RETURN_FALSE;
207 251 }
208 252 };
209 253
210 254 static PyObject *dirstate_item_get_merged(dirstateItemObject *self)
211 255 {
212 256 if (self->state == 'm') {
213 257 Py_RETURN_TRUE;
214 258 } else {
215 259 Py_RETURN_FALSE;
216 260 }
217 261 };
218 262
219 263 static PyObject *dirstate_item_get_merged_removed(dirstateItemObject *self)
220 264 {
221 265 if (self->state == 'r' && self->size == dirstate_v1_nonnormal) {
222 266 Py_RETURN_TRUE;
223 267 } else {
224 268 Py_RETURN_FALSE;
225 269 }
226 270 };
227 271
228 272 static PyObject *dirstate_item_get_from_p2(dirstateItemObject *self)
229 273 {
230 274 if (self->state == 'n' && self->size == dirstate_v1_from_p2) {
231 275 Py_RETURN_TRUE;
232 276 } else {
233 277 Py_RETURN_FALSE;
234 278 }
235 279 };
236 280
237 281 static PyObject *dirstate_item_get_from_p2_removed(dirstateItemObject *self)
238 282 {
239 283 if (self->state == 'r' && self->size == dirstate_v1_from_p2) {
240 284 Py_RETURN_TRUE;
241 285 } else {
242 286 Py_RETURN_FALSE;
243 287 }
244 288 };
245 289
246 290 static PyObject *dirstate_item_get_removed(dirstateItemObject *self)
247 291 {
248 292 if (self->state == 'r') {
249 293 Py_RETURN_TRUE;
250 294 } else {
251 295 Py_RETURN_FALSE;
252 296 }
253 297 };
254 298
255 299 static PyGetSetDef dirstate_item_getset[] = {
256 300 {"mode", (getter)dirstate_item_get_mode, NULL, "mode", NULL},
257 301 {"size", (getter)dirstate_item_get_size, NULL, "size", NULL},
258 302 {"mtime", (getter)dirstate_item_get_mtime, NULL, "mtime", NULL},
259 303 {"state", (getter)dirstate_item_get_state, NULL, "state", NULL},
260 304 {"tracked", (getter)dirstate_item_get_tracked, NULL, "tracked", NULL},
261 305 {"added", (getter)dirstate_item_get_added, NULL, "added", NULL},
262 306 {"merged_removed", (getter)dirstate_item_get_merged_removed, NULL,
263 307 "merged_removed", NULL},
264 308 {"merged", (getter)dirstate_item_get_merged, NULL, "merged", NULL},
265 309 {"from_p2_removed", (getter)dirstate_item_get_from_p2_removed, NULL,
266 310 "from_p2_removed", NULL},
267 311 {"from_p2", (getter)dirstate_item_get_from_p2, NULL, "from_p2", NULL},
268 312 {"removed", (getter)dirstate_item_get_removed, NULL, "removed", NULL},
269 313 {NULL} /* Sentinel */
270 314 };
271 315
272 316 PyTypeObject dirstateItemType = {
273 317 PyVarObject_HEAD_INIT(NULL, 0) /* header */
274 318 "dirstate_tuple", /* tp_name */
275 319 sizeof(dirstateItemObject), /* tp_basicsize */
276 320 0, /* tp_itemsize */
277 321 (destructor)dirstate_item_dealloc, /* tp_dealloc */
278 322 0, /* tp_print */
279 323 0, /* tp_getattr */
280 324 0, /* tp_setattr */
281 325 0, /* tp_compare */
282 326 0, /* tp_repr */
283 327 0, /* tp_as_number */
284 328 &dirstate_item_sq, /* tp_as_sequence */
285 329 0, /* tp_as_mapping */
286 330 0, /* tp_hash */
287 331 0, /* tp_call */
288 332 0, /* tp_str */
289 333 0, /* tp_getattro */
290 334 0, /* tp_setattro */
291 335 0, /* tp_as_buffer */
292 336 Py_TPFLAGS_DEFAULT, /* tp_flags */
293 337 "dirstate tuple", /* tp_doc */
294 338 0, /* tp_traverse */
295 339 0, /* tp_clear */
296 340 0, /* tp_richcompare */
297 341 0, /* tp_weaklistoffset */
298 342 0, /* tp_iter */
299 343 0, /* tp_iternext */
300 344 dirstate_item_methods, /* tp_methods */
301 345 0, /* tp_members */
302 346 dirstate_item_getset, /* tp_getset */
303 347 0, /* tp_base */
304 348 0, /* tp_dict */
305 349 0, /* tp_descr_get */
306 350 0, /* tp_descr_set */
307 351 0, /* tp_dictoffset */
308 352 0, /* tp_init */
309 353 0, /* tp_alloc */
310 354 dirstate_item_new, /* tp_new */
311 355 };
312 356
313 357 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
314 358 {
315 359 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
316 360 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
317 361 char state, *cur, *str, *cpos;
318 362 int mode, size, mtime;
319 363 unsigned int flen, pos = 40;
320 364 Py_ssize_t len = 40;
321 365 Py_ssize_t readlen;
322 366
323 367 if (!PyArg_ParseTuple(
324 368 args, PY23("O!O!s#:parse_dirstate", "O!O!y#:parse_dirstate"),
325 369 &PyDict_Type, &dmap, &PyDict_Type, &cmap, &str, &readlen)) {
326 370 goto quit;
327 371 }
328 372
329 373 len = readlen;
330 374
331 375 /* read parents */
332 376 if (len < 40) {
333 377 PyErr_SetString(PyExc_ValueError,
334 378 "too little data for parents");
335 379 goto quit;
336 380 }
337 381
338 382 parents = Py_BuildValue(PY23("s#s#", "y#y#"), str, (Py_ssize_t)20,
339 383 str + 20, (Py_ssize_t)20);
340 384 if (!parents) {
341 385 goto quit;
342 386 }
343 387
344 388 /* read filenames */
345 389 while (pos >= 40 && pos < len) {
346 390 if (pos + 17 > len) {
347 391 PyErr_SetString(PyExc_ValueError,
348 392 "overflow in dirstate");
349 393 goto quit;
350 394 }
351 395 cur = str + pos;
352 396 /* unpack header */
353 397 state = *cur;
354 398 mode = getbe32(cur + 1);
355 399 size = getbe32(cur + 5);
356 400 mtime = getbe32(cur + 9);
357 401 flen = getbe32(cur + 13);
358 402 pos += 17;
359 403 cur += 17;
360 404 if (flen > len - pos) {
361 405 PyErr_SetString(PyExc_ValueError,
362 406 "overflow in dirstate");
363 407 goto quit;
364 408 }
365 409
366 entry =
367 (PyObject *)make_dirstate_item(state, mode, size, mtime);
410 entry = (PyObject *)dirstate_item_from_v1_data(state, mode,
411 size, mtime);
368 412 cpos = memchr(cur, 0, flen);
369 413 if (cpos) {
370 414 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
371 415 cname = PyBytes_FromStringAndSize(
372 416 cpos + 1, flen - (cpos - cur) - 1);
373 417 if (!fname || !cname ||
374 418 PyDict_SetItem(cmap, fname, cname) == -1 ||
375 419 PyDict_SetItem(dmap, fname, entry) == -1) {
376 420 goto quit;
377 421 }
378 422 Py_DECREF(cname);
379 423 } else {
380 424 fname = PyBytes_FromStringAndSize(cur, flen);
381 425 if (!fname ||
382 426 PyDict_SetItem(dmap, fname, entry) == -1) {
383 427 goto quit;
384 428 }
385 429 }
386 430 Py_DECREF(fname);
387 431 Py_DECREF(entry);
388 432 fname = cname = entry = NULL;
389 433 pos += flen;
390 434 }
391 435
392 436 ret = parents;
393 437 Py_INCREF(ret);
394 438 quit:
395 439 Py_XDECREF(fname);
396 440 Py_XDECREF(cname);
397 441 Py_XDECREF(entry);
398 442 Py_XDECREF(parents);
399 443 return ret;
400 444 }
401 445
402 446 /*
403 447 * Build a set of non-normal and other parent entries from the dirstate dmap
404 448 */
405 449 static PyObject *nonnormalotherparententries(PyObject *self, PyObject *args)
406 450 {
407 451 PyObject *dmap, *fname, *v;
408 452 PyObject *nonnset = NULL, *otherpset = NULL, *result = NULL;
409 453 Py_ssize_t pos;
410 454
411 455 if (!PyArg_ParseTuple(args, "O!:nonnormalentries", &PyDict_Type,
412 456 &dmap)) {
413 457 goto bail;
414 458 }
415 459
416 460 nonnset = PySet_New(NULL);
417 461 if (nonnset == NULL) {
418 462 goto bail;
419 463 }
420 464
421 465 otherpset = PySet_New(NULL);
422 466 if (otherpset == NULL) {
423 467 goto bail;
424 468 }
425 469
426 470 pos = 0;
427 471 while (PyDict_Next(dmap, &pos, &fname, &v)) {
428 472 dirstateItemObject *t;
429 473 if (!dirstate_tuple_check(v)) {
430 474 PyErr_SetString(PyExc_TypeError,
431 475 "expected a dirstate tuple");
432 476 goto bail;
433 477 }
434 478 t = (dirstateItemObject *)v;
435 479
436 480 if (t->state == 'n' && t->size == -2) {
437 481 if (PySet_Add(otherpset, fname) == -1) {
438 482 goto bail;
439 483 }
440 484 }
441 485
442 486 if (t->state == 'n' && t->mtime != -1) {
443 487 continue;
444 488 }
445 489 if (PySet_Add(nonnset, fname) == -1) {
446 490 goto bail;
447 491 }
448 492 }
449 493
450 494 result = Py_BuildValue("(OO)", nonnset, otherpset);
451 495 if (result == NULL) {
452 496 goto bail;
453 497 }
454 498 Py_DECREF(nonnset);
455 499 Py_DECREF(otherpset);
456 500 return result;
457 501 bail:
458 502 Py_XDECREF(nonnset);
459 503 Py_XDECREF(otherpset);
460 504 Py_XDECREF(result);
461 505 return NULL;
462 506 }
463 507
464 508 /*
465 509 * Efficiently pack a dirstate object into its on-disk format.
466 510 */
467 511 static PyObject *pack_dirstate(PyObject *self, PyObject *args)
468 512 {
469 513 PyObject *packobj = NULL;
470 514 PyObject *map, *copymap, *pl, *mtime_unset = NULL;
471 515 Py_ssize_t nbytes, pos, l;
472 516 PyObject *k, *v = NULL, *pn;
473 517 char *p, *s;
474 518 int now;
475 519
476 520 if (!PyArg_ParseTuple(args, "O!O!O!i:pack_dirstate", &PyDict_Type, &map,
477 521 &PyDict_Type, &copymap, &PyTuple_Type, &pl,
478 522 &now)) {
479 523 return NULL;
480 524 }
481 525
482 526 if (PyTuple_Size(pl) != 2) {
483 527 PyErr_SetString(PyExc_TypeError, "expected 2-element tuple");
484 528 return NULL;
485 529 }
486 530
487 531 /* Figure out how much we need to allocate. */
488 532 for (nbytes = 40, pos = 0; PyDict_Next(map, &pos, &k, &v);) {
489 533 PyObject *c;
490 534 if (!PyBytes_Check(k)) {
491 535 PyErr_SetString(PyExc_TypeError, "expected string key");
492 536 goto bail;
493 537 }
494 538 nbytes += PyBytes_GET_SIZE(k) + 17;
495 539 c = PyDict_GetItem(copymap, k);
496 540 if (c) {
497 541 if (!PyBytes_Check(c)) {
498 542 PyErr_SetString(PyExc_TypeError,
499 543 "expected string key");
500 544 goto bail;
501 545 }
502 546 nbytes += PyBytes_GET_SIZE(c) + 1;
503 547 }
504 548 }
505 549
506 550 packobj = PyBytes_FromStringAndSize(NULL, nbytes);
507 551 if (packobj == NULL) {
508 552 goto bail;
509 553 }
510 554
511 555 p = PyBytes_AS_STRING(packobj);
512 556
513 557 pn = PyTuple_GET_ITEM(pl, 0);
514 558 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
515 559 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
516 560 goto bail;
517 561 }
518 562 memcpy(p, s, l);
519 563 p += 20;
520 564 pn = PyTuple_GET_ITEM(pl, 1);
521 565 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
522 566 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
523 567 goto bail;
524 568 }
525 569 memcpy(p, s, l);
526 570 p += 20;
527 571
528 572 for (pos = 0; PyDict_Next(map, &pos, &k, &v);) {
529 573 dirstateItemObject *tuple;
530 574 char state;
531 575 int mode, size, mtime;
532 576 Py_ssize_t len, l;
533 577 PyObject *o;
534 578 char *t;
535 579
536 580 if (!dirstate_tuple_check(v)) {
537 581 PyErr_SetString(PyExc_TypeError,
538 582 "expected a dirstate tuple");
539 583 goto bail;
540 584 }
541 585 tuple = (dirstateItemObject *)v;
542 586
543 587 state = tuple->state;
544 588 mode = tuple->mode;
545 589 size = tuple->size;
546 590 mtime = tuple->mtime;
547 591 if (state == 'n' && mtime == now) {
548 592 /* See pure/parsers.py:pack_dirstate for why we do
549 593 * this. */
550 594 mtime = -1;
551 595 mtime_unset = (PyObject *)make_dirstate_item(
552 596 state, mode, size, mtime);
553 597 if (!mtime_unset) {
554 598 goto bail;
555 599 }
556 600 if (PyDict_SetItem(map, k, mtime_unset) == -1) {
557 601 goto bail;
558 602 }
559 603 Py_DECREF(mtime_unset);
560 604 mtime_unset = NULL;
561 605 }
562 606 *p++ = state;
563 607 putbe32((uint32_t)mode, p);
564 608 putbe32((uint32_t)size, p + 4);
565 609 putbe32((uint32_t)mtime, p + 8);
566 610 t = p + 12;
567 611 p += 16;
568 612 len = PyBytes_GET_SIZE(k);
569 613 memcpy(p, PyBytes_AS_STRING(k), len);
570 614 p += len;
571 615 o = PyDict_GetItem(copymap, k);
572 616 if (o) {
573 617 *p++ = '\0';
574 618 l = PyBytes_GET_SIZE(o);
575 619 memcpy(p, PyBytes_AS_STRING(o), l);
576 620 p += l;
577 621 len += l + 1;
578 622 }
579 623 putbe32((uint32_t)len, t);
580 624 }
581 625
582 626 pos = p - PyBytes_AS_STRING(packobj);
583 627 if (pos != nbytes) {
584 628 PyErr_Format(PyExc_SystemError, "bad dirstate size: %ld != %ld",
585 629 (long)pos, (long)nbytes);
586 630 goto bail;
587 631 }
588 632
589 633 return packobj;
590 634 bail:
591 635 Py_XDECREF(mtime_unset);
592 636 Py_XDECREF(packobj);
593 637 Py_XDECREF(v);
594 638 return NULL;
595 639 }
596 640
597 641 #define BUMPED_FIX 1
598 642 #define USING_SHA_256 2
599 643 #define FM1_HEADER_SIZE (4 + 8 + 2 + 2 + 1 + 1 + 1)
600 644
601 645 static PyObject *readshas(const char *source, unsigned char num,
602 646 Py_ssize_t hashwidth)
603 647 {
604 648 int i;
605 649 PyObject *list = PyTuple_New(num);
606 650 if (list == NULL) {
607 651 return NULL;
608 652 }
609 653 for (i = 0; i < num; i++) {
610 654 PyObject *hash = PyBytes_FromStringAndSize(source, hashwidth);
611 655 if (hash == NULL) {
612 656 Py_DECREF(list);
613 657 return NULL;
614 658 }
615 659 PyTuple_SET_ITEM(list, i, hash);
616 660 source += hashwidth;
617 661 }
618 662 return list;
619 663 }
620 664
621 665 static PyObject *fm1readmarker(const char *databegin, const char *dataend,
622 666 uint32_t *msize)
623 667 {
624 668 const char *data = databegin;
625 669 const char *meta;
626 670
627 671 double mtime;
628 672 int16_t tz;
629 673 uint16_t flags;
630 674 unsigned char nsuccs, nparents, nmetadata;
631 675 Py_ssize_t hashwidth = 20;
632 676
633 677 PyObject *prec = NULL, *parents = NULL, *succs = NULL;
634 678 PyObject *metadata = NULL, *ret = NULL;
635 679 int i;
636 680
637 681 if (data + FM1_HEADER_SIZE > dataend) {
638 682 goto overflow;
639 683 }
640 684
641 685 *msize = getbe32(data);
642 686 data += 4;
643 687 mtime = getbefloat64(data);
644 688 data += 8;
645 689 tz = getbeint16(data);
646 690 data += 2;
647 691 flags = getbeuint16(data);
648 692 data += 2;
649 693
650 694 if (flags & USING_SHA_256) {
651 695 hashwidth = 32;
652 696 }
653 697
654 698 nsuccs = (unsigned char)(*data++);
655 699 nparents = (unsigned char)(*data++);
656 700 nmetadata = (unsigned char)(*data++);
657 701
658 702 if (databegin + *msize > dataend) {
659 703 goto overflow;
660 704 }
661 705 dataend = databegin + *msize; /* narrow down to marker size */
662 706
663 707 if (data + hashwidth > dataend) {
664 708 goto overflow;
665 709 }
666 710 prec = PyBytes_FromStringAndSize(data, hashwidth);
667 711 data += hashwidth;
668 712 if (prec == NULL) {
669 713 goto bail;
670 714 }
671 715
672 716 if (data + nsuccs * hashwidth > dataend) {
673 717 goto overflow;
674 718 }
675 719 succs = readshas(data, nsuccs, hashwidth);
676 720 if (succs == NULL) {
677 721 goto bail;
678 722 }
679 723 data += nsuccs * hashwidth;
680 724
681 725 if (nparents == 1 || nparents == 2) {
682 726 if (data + nparents * hashwidth > dataend) {
683 727 goto overflow;
684 728 }
685 729 parents = readshas(data, nparents, hashwidth);
686 730 if (parents == NULL) {
687 731 goto bail;
688 732 }
689 733 data += nparents * hashwidth;
690 734 } else {
691 735 parents = Py_None;
692 736 Py_INCREF(parents);
693 737 }
694 738
695 739 if (data + 2 * nmetadata > dataend) {
696 740 goto overflow;
697 741 }
698 742 meta = data + (2 * nmetadata);
699 743 metadata = PyTuple_New(nmetadata);
700 744 if (metadata == NULL) {
701 745 goto bail;
702 746 }
703 747 for (i = 0; i < nmetadata; i++) {
704 748 PyObject *tmp, *left = NULL, *right = NULL;
705 749 Py_ssize_t leftsize = (unsigned char)(*data++);
706 750 Py_ssize_t rightsize = (unsigned char)(*data++);
707 751 if (meta + leftsize + rightsize > dataend) {
708 752 goto overflow;
709 753 }
710 754 left = PyBytes_FromStringAndSize(meta, leftsize);
711 755 meta += leftsize;
712 756 right = PyBytes_FromStringAndSize(meta, rightsize);
713 757 meta += rightsize;
714 758 tmp = PyTuple_New(2);
715 759 if (!left || !right || !tmp) {
716 760 Py_XDECREF(left);
717 761 Py_XDECREF(right);
718 762 Py_XDECREF(tmp);
719 763 goto bail;
720 764 }
721 765 PyTuple_SET_ITEM(tmp, 0, left);
722 766 PyTuple_SET_ITEM(tmp, 1, right);
723 767 PyTuple_SET_ITEM(metadata, i, tmp);
724 768 }
725 769 ret = Py_BuildValue("(OOHO(di)O)", prec, succs, flags, metadata, mtime,
726 770 (int)tz * 60, parents);
727 771 goto bail; /* return successfully */
728 772
729 773 overflow:
730 774 PyErr_SetString(PyExc_ValueError, "overflow in obsstore");
731 775 bail:
732 776 Py_XDECREF(prec);
733 777 Py_XDECREF(succs);
734 778 Py_XDECREF(metadata);
735 779 Py_XDECREF(parents);
736 780 return ret;
737 781 }
738 782
739 783 static PyObject *fm1readmarkers(PyObject *self, PyObject *args)
740 784 {
741 785 const char *data, *dataend;
742 786 Py_ssize_t datalen, offset, stop;
743 787 PyObject *markers = NULL;
744 788
745 789 if (!PyArg_ParseTuple(args, PY23("s#nn", "y#nn"), &data, &datalen,
746 790 &offset, &stop)) {
747 791 return NULL;
748 792 }
749 793 if (offset < 0) {
750 794 PyErr_SetString(PyExc_ValueError,
751 795 "invalid negative offset in fm1readmarkers");
752 796 return NULL;
753 797 }
754 798 if (stop > datalen) {
755 799 PyErr_SetString(
756 800 PyExc_ValueError,
757 801 "stop longer than data length in fm1readmarkers");
758 802 return NULL;
759 803 }
760 804 dataend = data + datalen;
761 805 data += offset;
762 806 markers = PyList_New(0);
763 807 if (!markers) {
764 808 return NULL;
765 809 }
766 810 while (offset < stop) {
767 811 uint32_t msize;
768 812 int error;
769 813 PyObject *record = fm1readmarker(data, dataend, &msize);
770 814 if (!record) {
771 815 goto bail;
772 816 }
773 817 error = PyList_Append(markers, record);
774 818 Py_DECREF(record);
775 819 if (error) {
776 820 goto bail;
777 821 }
778 822 data += msize;
779 823 offset += msize;
780 824 }
781 825 return markers;
782 826 bail:
783 827 Py_DECREF(markers);
784 828 return NULL;
785 829 }
786 830
787 831 static char parsers_doc[] = "Efficient content parsing.";
788 832
789 833 PyObject *encodedir(PyObject *self, PyObject *args);
790 834 PyObject *pathencode(PyObject *self, PyObject *args);
791 835 PyObject *lowerencode(PyObject *self, PyObject *args);
792 836 PyObject *parse_index2(PyObject *self, PyObject *args, PyObject *kwargs);
793 837
794 838 static PyMethodDef methods[] = {
795 839 {"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"},
796 840 {"nonnormalotherparententries", nonnormalotherparententries, METH_VARARGS,
797 841 "create a set containing non-normal and other parent entries of given "
798 842 "dirstate\n"},
799 843 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
800 844 {"parse_index2", (PyCFunction)parse_index2, METH_VARARGS | METH_KEYWORDS,
801 845 "parse a revlog index\n"},
802 846 {"isasciistr", isasciistr, METH_VARARGS, "check if an ASCII string\n"},
803 847 {"asciilower", asciilower, METH_VARARGS, "lowercase an ASCII string\n"},
804 848 {"asciiupper", asciiupper, METH_VARARGS, "uppercase an ASCII string\n"},
805 849 {"dict_new_presized", dict_new_presized, METH_VARARGS,
806 850 "construct a dict with an expected size\n"},
807 851 {"make_file_foldmap", make_file_foldmap, METH_VARARGS,
808 852 "make file foldmap\n"},
809 853 {"jsonescapeu8fast", jsonescapeu8fast, METH_VARARGS,
810 854 "escape a UTF-8 byte string to JSON (fast path)\n"},
811 855 {"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"},
812 856 {"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"},
813 857 {"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"},
814 858 {"fm1readmarkers", fm1readmarkers, METH_VARARGS,
815 859 "parse v1 obsolete markers\n"},
816 860 {NULL, NULL}};
817 861
818 862 void dirs_module_init(PyObject *mod);
819 863 void manifest_module_init(PyObject *mod);
820 864 void revlog_module_init(PyObject *mod);
821 865
822 866 static const int version = 20;
823 867
824 868 static void module_init(PyObject *mod)
825 869 {
826 870 PyObject *capsule = NULL;
827 871 PyModule_AddIntConstant(mod, "version", version);
828 872
829 873 /* This module constant has two purposes. First, it lets us unit test
830 874 * the ImportError raised without hard-coding any error text. This
831 875 * means we can change the text in the future without breaking tests,
832 876 * even across changesets without a recompile. Second, its presence
833 877 * can be used to determine whether the version-checking logic is
834 878 * present, which also helps in testing across changesets without a
835 879 * recompile. Note that this means the pure-Python version of parsers
836 880 * should not have this module constant. */
837 881 PyModule_AddStringConstant(mod, "versionerrortext", versionerrortext);
838 882
839 883 dirs_module_init(mod);
840 884 manifest_module_init(mod);
841 885 revlog_module_init(mod);
842 886
843 887 capsule = PyCapsule_New(
844 888 make_dirstate_item,
845 889 "mercurial.cext.parsers.make_dirstate_item_CAPI", NULL);
846 890 if (capsule != NULL)
847 891 PyModule_AddObject(mod, "make_dirstate_item_CAPI", capsule);
848 892
849 893 if (PyType_Ready(&dirstateItemType) < 0) {
850 894 return;
851 895 }
852 896 Py_INCREF(&dirstateItemType);
853 897 PyModule_AddObject(mod, "DirstateItem", (PyObject *)&dirstateItemType);
854 898 }
855 899
856 900 static int check_python_version(void)
857 901 {
858 902 PyObject *sys = PyImport_ImportModule("sys"), *ver;
859 903 long hexversion;
860 904 if (!sys) {
861 905 return -1;
862 906 }
863 907 ver = PyObject_GetAttrString(sys, "hexversion");
864 908 Py_DECREF(sys);
865 909 if (!ver) {
866 910 return -1;
867 911 }
868 912 hexversion = PyInt_AsLong(ver);
869 913 Py_DECREF(ver);
870 914 /* sys.hexversion is a 32-bit number by default, so the -1 case
871 915 * should only occur in unusual circumstances (e.g. if sys.hexversion
872 916 * is manually set to an invalid value). */
873 917 if ((hexversion == -1) || (hexversion >> 16 != PY_VERSION_HEX >> 16)) {
874 918 PyErr_Format(PyExc_ImportError,
875 919 "%s: The Mercurial extension "
876 920 "modules were compiled with Python " PY_VERSION
877 921 ", but "
878 922 "Mercurial is currently using Python with "
879 923 "sys.hexversion=%ld: "
880 924 "Python %s\n at: %s",
881 925 versionerrortext, hexversion, Py_GetVersion(),
882 926 Py_GetProgramFullPath());
883 927 return -1;
884 928 }
885 929 return 0;
886 930 }
887 931
888 932 #ifdef IS_PY3K
889 933 static struct PyModuleDef parsers_module = {PyModuleDef_HEAD_INIT, "parsers",
890 934 parsers_doc, -1, methods};
891 935
892 936 PyMODINIT_FUNC PyInit_parsers(void)
893 937 {
894 938 PyObject *mod;
895 939
896 940 if (check_python_version() == -1)
897 941 return NULL;
898 942 mod = PyModule_Create(&parsers_module);
899 943 module_init(mod);
900 944 return mod;
901 945 }
902 946 #else
903 947 PyMODINIT_FUNC initparsers(void)
904 948 {
905 949 PyObject *mod;
906 950
907 951 if (check_python_version() == -1) {
908 952 return;
909 953 }
910 954 mod = Py_InitModule3("parsers", methods, parsers_doc);
911 955 module_init(mod);
912 956 }
913 957 #endif
@@ -1,584 +1,598
1 1 # parsers.py - Python implementation of parsers.c
2 2 #
3 3 # Copyright 2009 Olivia Mackall <olivia@selenic.com> and others
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 from __future__ import absolute_import
9 9
10 10 import struct
11 11 import zlib
12 12
13 13 from ..node import (
14 14 nullrev,
15 15 sha1nodeconstants,
16 16 )
17 17 from ..thirdparty import attr
18 18 from .. import (
19 19 error,
20 20 pycompat,
21 21 revlogutils,
22 22 util,
23 23 )
24 24
25 25 from ..revlogutils import nodemap as nodemaputil
26 26 from ..revlogutils import constants as revlog_constants
27 27
28 28 stringio = pycompat.bytesio
29 29
30 30
31 31 _pack = struct.pack
32 32 _unpack = struct.unpack
33 33 _compress = zlib.compress
34 34 _decompress = zlib.decompress
35 35
36 36
37 37 # a special value used internally for `size` if the file come from the other parent
38 38 FROM_P2 = -2
39 39
40 40 # a special value used internally for `size` if the file is modified/merged/added
41 41 NONNORMAL = -1
42 42
43 43 # a special value used internally for `time` if the time is ambigeous
44 44 AMBIGUOUS_TIME = -1
45 45
46 46
47 47 @attr.s(slots=True, init=False)
48 48 class DirstateItem(object):
49 49 """represent a dirstate entry
50 50
51 51 It contains:
52 52
53 53 - state (one of 'n', 'a', 'r', 'm')
54 54 - mode,
55 55 - size,
56 56 - mtime,
57 57 """
58 58
59 59 _state = attr.ib()
60 60 _mode = attr.ib()
61 61 _size = attr.ib()
62 62 _mtime = attr.ib()
63 63
64 64 def __init__(self, state, mode, size, mtime):
65 65 self._state = state
66 66 self._mode = mode
67 67 self._size = size
68 68 self._mtime = mtime
69 69
70 @classmethod
71 def from_v1_data(cls, state, mode, size, mtime):
72 """Build a new DirstateItem object from V1 data
73
74 Since the dirstate-v1 format is frozen, the signature of this function
75 is not expected to change, unlike the __init__ one.
76 """
77 return cls(
78 state=state,
79 mode=mode,
80 size=size,
81 mtime=mtime,
82 )
83
70 84 def __getitem__(self, idx):
71 85 if idx == 0 or idx == -4:
72 86 msg = b"do not use item[x], use item.state"
73 87 util.nouideprecwarn(msg, b'6.0', stacklevel=2)
74 88 return self._state
75 89 elif idx == 1 or idx == -3:
76 90 msg = b"do not use item[x], use item.mode"
77 91 util.nouideprecwarn(msg, b'6.0', stacklevel=2)
78 92 return self._mode
79 93 elif idx == 2 or idx == -2:
80 94 msg = b"do not use item[x], use item.size"
81 95 util.nouideprecwarn(msg, b'6.0', stacklevel=2)
82 96 return self._size
83 97 elif idx == 3 or idx == -1:
84 98 msg = b"do not use item[x], use item.mtime"
85 99 util.nouideprecwarn(msg, b'6.0', stacklevel=2)
86 100 return self._mtime
87 101 else:
88 102 raise IndexError(idx)
89 103
90 104 @property
91 105 def mode(self):
92 106 return self._mode
93 107
94 108 @property
95 109 def size(self):
96 110 return self._size
97 111
98 112 @property
99 113 def mtime(self):
100 114 return self._mtime
101 115
102 116 @property
103 117 def state(self):
104 118 """
105 119 States are:
106 120 n normal
107 121 m needs merging
108 122 r marked for removal
109 123 a marked for addition
110 124
111 125 XXX This "state" is a bit obscure and mostly a direct expression of the
112 126 dirstatev1 format. It would make sense to ultimately deprecate it in
113 127 favor of the more "semantic" attributes.
114 128 """
115 129 return self._state
116 130
117 131 @property
118 132 def tracked(self):
119 133 """True is the file is tracked in the working copy"""
120 134 return self._state in b"nma"
121 135
122 136 @property
123 137 def added(self):
124 138 """True if the file has been added"""
125 139 return self._state == b'a'
126 140
127 141 @property
128 142 def merged(self):
129 143 """True if the file has been merged
130 144
131 145 Should only be set if a merge is in progress in the dirstate
132 146 """
133 147 return self._state == b'm'
134 148
135 149 @property
136 150 def from_p2(self):
137 151 """True if the file have been fetched from p2 during the current merge
138 152
139 153 This is only True is the file is currently tracked.
140 154
141 155 Should only be set if a merge is in progress in the dirstate
142 156 """
143 157 return self._state == b'n' and self._size == FROM_P2
144 158
145 159 @property
146 160 def from_p2_removed(self):
147 161 """True if the file has been removed, but was "from_p2" initially
148 162
149 163 This property seems like an abstraction leakage and should probably be
150 164 dealt in this class (or maybe the dirstatemap) directly.
151 165 """
152 166 return self._state == b'r' and self._size == FROM_P2
153 167
154 168 @property
155 169 def removed(self):
156 170 """True if the file has been removed"""
157 171 return self._state == b'r'
158 172
159 173 @property
160 174 def merged_removed(self):
161 175 """True if the file has been removed, but was "merged" initially
162 176
163 177 This property seems like an abstraction leakage and should probably be
164 178 dealt in this class (or maybe the dirstatemap) directly.
165 179 """
166 180 return self._state == b'r' and self._size == NONNORMAL
167 181
168 182 def v1_state(self):
169 183 """return a "state" suitable for v1 serialization"""
170 184 return self._state
171 185
172 186 def v1_mode(self):
173 187 """return a "mode" suitable for v1 serialization"""
174 188 return self._mode
175 189
176 190 def v1_size(self):
177 191 """return a "size" suitable for v1 serialization"""
178 192 return self._size
179 193
180 194 def v1_mtime(self):
181 195 """return a "mtime" suitable for v1 serialization"""
182 196 return self._mtime
183 197
184 198 def need_delay(self, now):
185 199 """True if the stored mtime would be ambiguous with the current time"""
186 200 return self._state == b'n' and self._mtime == now
187 201
188 202
189 203 def gettype(q):
190 204 return int(q & 0xFFFF)
191 205
192 206
193 207 class BaseIndexObject(object):
194 208 # Can I be passed to an algorithme implemented in Rust ?
195 209 rust_ext_compat = 0
196 210 # Format of an index entry according to Python's `struct` language
197 211 index_format = revlog_constants.INDEX_ENTRY_V1
198 212 # Size of a C unsigned long long int, platform independent
199 213 big_int_size = struct.calcsize(b'>Q')
200 214 # Size of a C long int, platform independent
201 215 int_size = struct.calcsize(b'>i')
202 216 # An empty index entry, used as a default value to be overridden, or nullrev
203 217 null_item = (
204 218 0,
205 219 0,
206 220 0,
207 221 -1,
208 222 -1,
209 223 -1,
210 224 -1,
211 225 sha1nodeconstants.nullid,
212 226 0,
213 227 0,
214 228 revlog_constants.COMP_MODE_INLINE,
215 229 revlog_constants.COMP_MODE_INLINE,
216 230 )
217 231
218 232 @util.propertycache
219 233 def entry_size(self):
220 234 return self.index_format.size
221 235
222 236 @property
223 237 def nodemap(self):
224 238 msg = b"index.nodemap is deprecated, use index.[has_node|rev|get_rev]"
225 239 util.nouideprecwarn(msg, b'5.3', stacklevel=2)
226 240 return self._nodemap
227 241
228 242 @util.propertycache
229 243 def _nodemap(self):
230 244 nodemap = nodemaputil.NodeMap({sha1nodeconstants.nullid: nullrev})
231 245 for r in range(0, len(self)):
232 246 n = self[r][7]
233 247 nodemap[n] = r
234 248 return nodemap
235 249
236 250 def has_node(self, node):
237 251 """return True if the node exist in the index"""
238 252 return node in self._nodemap
239 253
240 254 def rev(self, node):
241 255 """return a revision for a node
242 256
243 257 If the node is unknown, raise a RevlogError"""
244 258 return self._nodemap[node]
245 259
246 260 def get_rev(self, node):
247 261 """return a revision for a node
248 262
249 263 If the node is unknown, return None"""
250 264 return self._nodemap.get(node)
251 265
252 266 def _stripnodes(self, start):
253 267 if '_nodemap' in vars(self):
254 268 for r in range(start, len(self)):
255 269 n = self[r][7]
256 270 del self._nodemap[n]
257 271
258 272 def clearcaches(self):
259 273 self.__dict__.pop('_nodemap', None)
260 274
261 275 def __len__(self):
262 276 return self._lgt + len(self._extra)
263 277
264 278 def append(self, tup):
265 279 if '_nodemap' in vars(self):
266 280 self._nodemap[tup[7]] = len(self)
267 281 data = self._pack_entry(len(self), tup)
268 282 self._extra.append(data)
269 283
270 284 def _pack_entry(self, rev, entry):
271 285 assert entry[8] == 0
272 286 assert entry[9] == 0
273 287 return self.index_format.pack(*entry[:8])
274 288
275 289 def _check_index(self, i):
276 290 if not isinstance(i, int):
277 291 raise TypeError(b"expecting int indexes")
278 292 if i < 0 or i >= len(self):
279 293 raise IndexError
280 294
281 295 def __getitem__(self, i):
282 296 if i == -1:
283 297 return self.null_item
284 298 self._check_index(i)
285 299 if i >= self._lgt:
286 300 data = self._extra[i - self._lgt]
287 301 else:
288 302 index = self._calculate_index(i)
289 303 data = self._data[index : index + self.entry_size]
290 304 r = self._unpack_entry(i, data)
291 305 if self._lgt and i == 0:
292 306 offset = revlogutils.offset_type(0, gettype(r[0]))
293 307 r = (offset,) + r[1:]
294 308 return r
295 309
296 310 def _unpack_entry(self, rev, data):
297 311 r = self.index_format.unpack(data)
298 312 r = r + (
299 313 0,
300 314 0,
301 315 revlog_constants.COMP_MODE_INLINE,
302 316 revlog_constants.COMP_MODE_INLINE,
303 317 )
304 318 return r
305 319
306 320 def pack_header(self, header):
307 321 """pack header information as binary"""
308 322 v_fmt = revlog_constants.INDEX_HEADER
309 323 return v_fmt.pack(header)
310 324
311 325 def entry_binary(self, rev):
312 326 """return the raw binary string representing a revision"""
313 327 entry = self[rev]
314 328 p = revlog_constants.INDEX_ENTRY_V1.pack(*entry[:8])
315 329 if rev == 0:
316 330 p = p[revlog_constants.INDEX_HEADER.size :]
317 331 return p
318 332
319 333
320 334 class IndexObject(BaseIndexObject):
321 335 def __init__(self, data):
322 336 assert len(data) % self.entry_size == 0, (
323 337 len(data),
324 338 self.entry_size,
325 339 len(data) % self.entry_size,
326 340 )
327 341 self._data = data
328 342 self._lgt = len(data) // self.entry_size
329 343 self._extra = []
330 344
331 345 def _calculate_index(self, i):
332 346 return i * self.entry_size
333 347
334 348 def __delitem__(self, i):
335 349 if not isinstance(i, slice) or not i.stop == -1 or i.step is not None:
336 350 raise ValueError(b"deleting slices only supports a:-1 with step 1")
337 351 i = i.start
338 352 self._check_index(i)
339 353 self._stripnodes(i)
340 354 if i < self._lgt:
341 355 self._data = self._data[: i * self.entry_size]
342 356 self._lgt = i
343 357 self._extra = []
344 358 else:
345 359 self._extra = self._extra[: i - self._lgt]
346 360
347 361
348 362 class PersistentNodeMapIndexObject(IndexObject):
349 363 """a Debug oriented class to test persistent nodemap
350 364
351 365 We need a simple python object to test API and higher level behavior. See
352 366 the Rust implementation for more serious usage. This should be used only
353 367 through the dedicated `devel.persistent-nodemap` config.
354 368 """
355 369
356 370 def nodemap_data_all(self):
357 371 """Return bytes containing a full serialization of a nodemap
358 372
359 373 The nodemap should be valid for the full set of revisions in the
360 374 index."""
361 375 return nodemaputil.persistent_data(self)
362 376
363 377 def nodemap_data_incremental(self):
364 378 """Return bytes containing a incremental update to persistent nodemap
365 379
366 380 This containst the data for an append-only update of the data provided
367 381 in the last call to `update_nodemap_data`.
368 382 """
369 383 if self._nm_root is None:
370 384 return None
371 385 docket = self._nm_docket
372 386 changed, data = nodemaputil.update_persistent_data(
373 387 self, self._nm_root, self._nm_max_idx, self._nm_docket.tip_rev
374 388 )
375 389
376 390 self._nm_root = self._nm_max_idx = self._nm_docket = None
377 391 return docket, changed, data
378 392
379 393 def update_nodemap_data(self, docket, nm_data):
380 394 """provide full block of persisted binary data for a nodemap
381 395
382 396 The data are expected to come from disk. See `nodemap_data_all` for a
383 397 produceur of such data."""
384 398 if nm_data is not None:
385 399 self._nm_root, self._nm_max_idx = nodemaputil.parse_data(nm_data)
386 400 if self._nm_root:
387 401 self._nm_docket = docket
388 402 else:
389 403 self._nm_root = self._nm_max_idx = self._nm_docket = None
390 404
391 405
392 406 class InlinedIndexObject(BaseIndexObject):
393 407 def __init__(self, data, inline=0):
394 408 self._data = data
395 409 self._lgt = self._inline_scan(None)
396 410 self._inline_scan(self._lgt)
397 411 self._extra = []
398 412
399 413 def _inline_scan(self, lgt):
400 414 off = 0
401 415 if lgt is not None:
402 416 self._offsets = [0] * lgt
403 417 count = 0
404 418 while off <= len(self._data) - self.entry_size:
405 419 start = off + self.big_int_size
406 420 (s,) = struct.unpack(
407 421 b'>i',
408 422 self._data[start : start + self.int_size],
409 423 )
410 424 if lgt is not None:
411 425 self._offsets[count] = off
412 426 count += 1
413 427 off += self.entry_size + s
414 428 if off != len(self._data):
415 429 raise ValueError(b"corrupted data")
416 430 return count
417 431
418 432 def __delitem__(self, i):
419 433 if not isinstance(i, slice) or not i.stop == -1 or i.step is not None:
420 434 raise ValueError(b"deleting slices only supports a:-1 with step 1")
421 435 i = i.start
422 436 self._check_index(i)
423 437 self._stripnodes(i)
424 438 if i < self._lgt:
425 439 self._offsets = self._offsets[:i]
426 440 self._lgt = i
427 441 self._extra = []
428 442 else:
429 443 self._extra = self._extra[: i - self._lgt]
430 444
431 445 def _calculate_index(self, i):
432 446 return self._offsets[i]
433 447
434 448
435 449 def parse_index2(data, inline, revlogv2=False):
436 450 if not inline:
437 451 cls = IndexObject2 if revlogv2 else IndexObject
438 452 return cls(data), None
439 453 cls = InlinedIndexObject
440 454 return cls(data, inline), (0, data)
441 455
442 456
443 457 def parse_index_cl_v2(data):
444 458 return IndexChangelogV2(data), None
445 459
446 460
447 461 class IndexObject2(IndexObject):
448 462 index_format = revlog_constants.INDEX_ENTRY_V2
449 463
450 464 def replace_sidedata_info(
451 465 self,
452 466 rev,
453 467 sidedata_offset,
454 468 sidedata_length,
455 469 offset_flags,
456 470 compression_mode,
457 471 ):
458 472 """
459 473 Replace an existing index entry's sidedata offset and length with new
460 474 ones.
461 475 This cannot be used outside of the context of sidedata rewriting,
462 476 inside the transaction that creates the revision `rev`.
463 477 """
464 478 if rev < 0:
465 479 raise KeyError
466 480 self._check_index(rev)
467 481 if rev < self._lgt:
468 482 msg = b"cannot rewrite entries outside of this transaction"
469 483 raise KeyError(msg)
470 484 else:
471 485 entry = list(self[rev])
472 486 entry[0] = offset_flags
473 487 entry[8] = sidedata_offset
474 488 entry[9] = sidedata_length
475 489 entry[11] = compression_mode
476 490 entry = tuple(entry)
477 491 new = self._pack_entry(rev, entry)
478 492 self._extra[rev - self._lgt] = new
479 493
480 494 def _unpack_entry(self, rev, data):
481 495 data = self.index_format.unpack(data)
482 496 entry = data[:10]
483 497 data_comp = data[10] & 3
484 498 sidedata_comp = (data[10] & (3 << 2)) >> 2
485 499 return entry + (data_comp, sidedata_comp)
486 500
487 501 def _pack_entry(self, rev, entry):
488 502 data = entry[:10]
489 503 data_comp = entry[10] & 3
490 504 sidedata_comp = (entry[11] & 3) << 2
491 505 data += (data_comp | sidedata_comp,)
492 506
493 507 return self.index_format.pack(*data)
494 508
495 509 def entry_binary(self, rev):
496 510 """return the raw binary string representing a revision"""
497 511 entry = self[rev]
498 512 return self._pack_entry(rev, entry)
499 513
500 514 def pack_header(self, header):
501 515 """pack header information as binary"""
502 516 msg = 'version header should go in the docket, not the index: %d'
503 517 msg %= header
504 518 raise error.ProgrammingError(msg)
505 519
506 520
507 521 class IndexChangelogV2(IndexObject2):
508 522 index_format = revlog_constants.INDEX_ENTRY_CL_V2
509 523
510 524 def _unpack_entry(self, rev, data, r=True):
511 525 items = self.index_format.unpack(data)
512 526 entry = items[:3] + (rev, rev) + items[3:8]
513 527 data_comp = items[8] & 3
514 528 sidedata_comp = (items[8] >> 2) & 3
515 529 return entry + (data_comp, sidedata_comp)
516 530
517 531 def _pack_entry(self, rev, entry):
518 532 assert entry[3] == rev, entry[3]
519 533 assert entry[4] == rev, entry[4]
520 534 data = entry[:3] + entry[5:10]
521 535 data_comp = entry[10] & 3
522 536 sidedata_comp = (entry[11] & 3) << 2
523 537 data += (data_comp | sidedata_comp,)
524 538 return self.index_format.pack(*data)
525 539
526 540
527 541 def parse_index_devel_nodemap(data, inline):
528 542 """like parse_index2, but alway return a PersistentNodeMapIndexObject"""
529 543 return PersistentNodeMapIndexObject(data), None
530 544
531 545
532 546 def parse_dirstate(dmap, copymap, st):
533 547 parents = [st[:20], st[20:40]]
534 548 # dereference fields so they will be local in loop
535 549 format = b">cllll"
536 550 e_size = struct.calcsize(format)
537 551 pos1 = 40
538 552 l = len(st)
539 553
540 554 # the inner loop
541 555 while pos1 < l:
542 556 pos2 = pos1 + e_size
543 557 e = _unpack(b">cllll", st[pos1:pos2]) # a literal here is faster
544 558 pos1 = pos2 + e[4]
545 559 f = st[pos2:pos1]
546 560 if b'\0' in f:
547 561 f, c = f.split(b'\0')
548 562 copymap[f] = c
549 dmap[f] = DirstateItem(*e[:4])
563 dmap[f] = DirstateItem.from_v1_data(*e[:4])
550 564 return parents
551 565
552 566
553 567 def pack_dirstate(dmap, copymap, pl, now):
554 568 now = int(now)
555 569 cs = stringio()
556 570 write = cs.write
557 571 write(b"".join(pl))
558 572 for f, e in pycompat.iteritems(dmap):
559 573 if e.need_delay(now):
560 574 # The file was last modified "simultaneously" with the current
561 575 # write to dirstate (i.e. within the same second for file-
562 576 # systems with a granularity of 1 sec). This commonly happens
563 577 # for at least a couple of files on 'update'.
564 578 # The user could change the file without changing its size
565 579 # within the same second. Invalidate the file's mtime in
566 580 # dirstate, forcing future 'status' calls to compare the
567 581 # contents of the file if the size is the same. This prevents
568 582 # mistakenly treating such files as clean.
569 583 e = DirstateItem(e.state, e.mode, e.size, AMBIGUOUS_TIME)
570 584 dmap[f] = e
571 585
572 586 if f in copymap:
573 587 f = b"%s\0%s" % (f, copymap[f])
574 588 e = _pack(
575 589 b">cllll",
576 590 e.v1_state(),
577 591 e.v1_mode(),
578 592 e.v1_size(),
579 593 e.v1_mtime(),
580 594 len(f),
581 595 )
582 596 write(e)
583 597 write(f)
584 598 return cs.getvalue()
General Comments 0
You need to be logged in to leave comments. Login now