##// END OF EJS Templates
dirstate: Remove the `state == ' '` special case...
Simon Sapin -
r48837:631f6b44 default
parent child Browse files
Show More
@@ -1,1333 +1,1320 b''
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 static const int ambiguous_time = -1;
35 35
36 36 static PyObject *dict_new_presized(PyObject *self, PyObject *args)
37 37 {
38 38 Py_ssize_t expected_size;
39 39
40 40 if (!PyArg_ParseTuple(args, "n:make_presized_dict", &expected_size)) {
41 41 return NULL;
42 42 }
43 43
44 44 return _dict_new_presized(expected_size);
45 45 }
46 46
47 47 static PyObject *dirstate_item_new(PyTypeObject *subtype, PyObject *args,
48 48 PyObject *kwds)
49 49 {
50 50 /* We do all the initialization here and not a tp_init function because
51 51 * dirstate_item is immutable. */
52 52 dirstateItemObject *t;
53 53 int wc_tracked;
54 54 int p1_tracked;
55 55 int p2_tracked;
56 56 int merged;
57 57 int clean_p1;
58 58 int clean_p2;
59 59 int possibly_dirty;
60 60 PyObject *parentfiledata;
61 61 static char *keywords_name[] = {
62 62 "wc_tracked", "p1_tracked", "p2_tracked",
63 63 "merged", "clean_p1", "clean_p2",
64 64 "possibly_dirty", "parentfiledata", NULL,
65 65 };
66 66 wc_tracked = 0;
67 67 p1_tracked = 0;
68 68 p2_tracked = 0;
69 69 merged = 0;
70 70 clean_p1 = 0;
71 71 clean_p2 = 0;
72 72 possibly_dirty = 0;
73 73 parentfiledata = Py_None;
74 74 if (!PyArg_ParseTupleAndKeywords(args, kwds, "iiiiiiiO", keywords_name,
75 75 &wc_tracked, &p1_tracked, &p2_tracked,
76 76 &merged, &clean_p1, &clean_p2,
77 77 &possibly_dirty, &parentfiledata
78 78
79 79 )) {
80 80 return NULL;
81 81 }
82 82 if (merged && (clean_p1 || clean_p2)) {
83 83 PyErr_SetString(PyExc_RuntimeError,
84 84 "`merged` argument incompatible with "
85 85 "`clean_p1`/`clean_p2`");
86 86 return NULL;
87 87 }
88 88 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
89 89 if (!t) {
90 90 return NULL;
91 91 }
92 92
93 93 t->flags = 0;
94 94 if (wc_tracked) {
95 95 t->flags |= dirstate_flag_wc_tracked;
96 96 }
97 97 if (p1_tracked) {
98 98 t->flags |= dirstate_flag_p1_tracked;
99 99 }
100 100 if (p2_tracked) {
101 101 t->flags |= dirstate_flag_p2_tracked;
102 102 }
103 103 if (possibly_dirty) {
104 104 t->flags |= dirstate_flag_possibly_dirty;
105 105 }
106 106 if (merged) {
107 107 t->flags |= dirstate_flag_merged;
108 108 }
109 109 if (clean_p1) {
110 110 t->flags |= dirstate_flag_clean_p1;
111 111 }
112 112 if (clean_p2) {
113 113 t->flags |= dirstate_flag_clean_p2;
114 114 }
115 115 t->mode = 0;
116 116 t->size = dirstate_v1_nonnormal;
117 117 t->mtime = ambiguous_time;
118 118 if (parentfiledata != Py_None) {
119 119 if (!PyTuple_CheckExact(parentfiledata)) {
120 120 PyErr_SetString(
121 121 PyExc_TypeError,
122 122 "parentfiledata should be a Tuple or None");
123 123 return NULL;
124 124 }
125 125 t->mode =
126 126 (int)PyLong_AsLong(PyTuple_GetItem(parentfiledata, 0));
127 127 t->size =
128 128 (int)PyLong_AsLong(PyTuple_GetItem(parentfiledata, 1));
129 129 t->mtime =
130 130 (int)PyLong_AsLong(PyTuple_GetItem(parentfiledata, 2));
131 131 }
132 132 return (PyObject *)t;
133 133 }
134 134
135 135 static void dirstate_item_dealloc(PyObject *o)
136 136 {
137 137 PyObject_Del(o);
138 138 }
139 139
140 140 static inline bool dirstate_item_c_tracked(dirstateItemObject *self)
141 141 {
142 142 return (self->flags & dirstate_flag_wc_tracked);
143 143 }
144 144
145 145 static inline bool dirstate_item_c_added(dirstateItemObject *self)
146 146 {
147 147 unsigned char mask =
148 148 (dirstate_flag_wc_tracked | dirstate_flag_p1_tracked |
149 149 dirstate_flag_p2_tracked);
150 150 unsigned char target = dirstate_flag_wc_tracked;
151 151 return (self->flags & mask) == target;
152 152 }
153 153
154 154 static inline bool dirstate_item_c_removed(dirstateItemObject *self)
155 155 {
156 156 if (self->flags & dirstate_flag_wc_tracked) {
157 157 return false;
158 158 }
159 159 return (self->flags &
160 160 (dirstate_flag_p1_tracked | dirstate_flag_p2_tracked));
161 161 }
162 162
163 163 static inline bool dirstate_item_c_merged(dirstateItemObject *self)
164 164 {
165 165 return ((self->flags & dirstate_flag_wc_tracked) &&
166 166 (self->flags & dirstate_flag_merged));
167 167 }
168 168
169 169 static inline bool dirstate_item_c_merged_removed(dirstateItemObject *self)
170 170 {
171 171 if (!dirstate_item_c_removed(self)) {
172 172 return false;
173 173 }
174 174 return (self->flags & dirstate_flag_merged);
175 175 }
176 176
177 177 static inline bool dirstate_item_c_from_p2(dirstateItemObject *self)
178 178 {
179 179 if (!dirstate_item_c_tracked(self)) {
180 180 return false;
181 181 }
182 182 return (self->flags & dirstate_flag_clean_p2);
183 183 }
184 184
185 185 static inline bool dirstate_item_c_from_p2_removed(dirstateItemObject *self)
186 186 {
187 187 if (!dirstate_item_c_removed(self)) {
188 188 return false;
189 189 }
190 190 return (self->flags & dirstate_flag_clean_p2);
191 191 }
192 192
193 193 static inline char dirstate_item_c_v1_state(dirstateItemObject *self)
194 194 {
195 if (self->flags & dirstate_flag_rust_special) {
196 return ' ';
197 } else if (dirstate_item_c_removed(self)) {
195 if (dirstate_item_c_removed(self)) {
198 196 return 'r';
199 197 } else if (dirstate_item_c_merged(self)) {
200 198 return 'm';
201 199 } else if (dirstate_item_c_added(self)) {
202 200 return 'a';
203 201 } else {
204 202 return 'n';
205 203 }
206 204 }
207 205
208 206 static inline int dirstate_item_c_v1_mode(dirstateItemObject *self)
209 207 {
210 208 return self->mode;
211 209 }
212 210
213 211 static inline int dirstate_item_c_v1_size(dirstateItemObject *self)
214 212 {
215 if (self->flags & dirstate_flag_rust_special) {
216 return self->size;
217 } else if (dirstate_item_c_merged_removed(self)) {
213 if (dirstate_item_c_merged_removed(self)) {
218 214 return dirstate_v1_nonnormal;
219 215 } else if (dirstate_item_c_from_p2_removed(self)) {
220 216 return dirstate_v1_from_p2;
221 217 } else if (dirstate_item_c_removed(self)) {
222 218 return 0;
223 219 } else if (dirstate_item_c_merged(self)) {
224 220 return dirstate_v1_from_p2;
225 221 } else if (dirstate_item_c_added(self)) {
226 222 return dirstate_v1_nonnormal;
227 223 } else if (dirstate_item_c_from_p2(self)) {
228 224 return dirstate_v1_from_p2;
229 225 } else if (self->flags & dirstate_flag_possibly_dirty) {
230 226 return self->size; /* NON NORMAL ? */
231 227 } else {
232 228 return self->size;
233 229 }
234 230 }
235 231
236 232 static inline int dirstate_item_c_v1_mtime(dirstateItemObject *self)
237 233 {
238 if (self->flags & dirstate_flag_rust_special) {
239 return self->mtime;
240 } else if (dirstate_item_c_removed(self)) {
234 if (dirstate_item_c_removed(self)) {
241 235 return 0;
242 236 } else if (self->flags & dirstate_flag_possibly_dirty) {
243 237 return ambiguous_time;
244 238 } else if (dirstate_item_c_merged(self)) {
245 239 return ambiguous_time;
246 240 } else if (dirstate_item_c_added(self)) {
247 241 return ambiguous_time;
248 242 } else if (dirstate_item_c_from_p2(self)) {
249 243 return ambiguous_time;
250 244 } else {
251 245 return self->mtime;
252 246 }
253 247 }
254 248
255 249 static PyObject *dirstate_item_v1_state(dirstateItemObject *self)
256 250 {
257 251 char state = dirstate_item_c_v1_state(self);
258 252 return PyBytes_FromStringAndSize(&state, 1);
259 253 };
260 254
261 255 static PyObject *dirstate_item_v1_mode(dirstateItemObject *self)
262 256 {
263 257 return PyInt_FromLong(dirstate_item_c_v1_mode(self));
264 258 };
265 259
266 260 static PyObject *dirstate_item_v1_size(dirstateItemObject *self)
267 261 {
268 262 return PyInt_FromLong(dirstate_item_c_v1_size(self));
269 263 };
270 264
271 265 static PyObject *dirstate_item_v1_mtime(dirstateItemObject *self)
272 266 {
273 267 return PyInt_FromLong(dirstate_item_c_v1_mtime(self));
274 268 };
275 269
276 270 static PyObject *dirstate_item_need_delay(dirstateItemObject *self,
277 271 PyObject *value)
278 272 {
279 273 long now;
280 274 if (!pylong_to_long(value, &now)) {
281 275 return NULL;
282 276 }
283 277 if (dirstate_item_c_v1_state(self) == 'n' &&
284 278 dirstate_item_c_v1_mtime(self) == now) {
285 279 Py_RETURN_TRUE;
286 280 } else {
287 281 Py_RETURN_FALSE;
288 282 }
289 283 };
290 284
291 285 /* This will never change since it's bound to V1
292 286 */
293 287 static inline dirstateItemObject *
294 288 dirstate_item_from_v1_data(char state, int mode, int size, int mtime)
295 289 {
296 290 dirstateItemObject *t =
297 291 PyObject_New(dirstateItemObject, &dirstateItemType);
298 292 if (!t) {
299 293 return NULL;
300 294 }
301 295
302 296 if (state == 'm') {
303 297 t->flags =
304 298 (dirstate_flag_wc_tracked | dirstate_flag_p1_tracked |
305 299 dirstate_flag_p2_tracked | dirstate_flag_merged);
306 300 t->mode = 0;
307 301 t->size = dirstate_v1_from_p2;
308 302 t->mtime = ambiguous_time;
309 303 } else if (state == 'a') {
310 304 t->flags = dirstate_flag_wc_tracked;
311 305 t->mode = 0;
312 306 t->size = dirstate_v1_nonnormal;
313 307 t->mtime = ambiguous_time;
314 308 } else if (state == 'r') {
315 309 t->mode = 0;
316 310 t->size = 0;
317 311 t->mtime = 0;
318 312 if (size == dirstate_v1_nonnormal) {
319 313 t->flags =
320 314 (dirstate_flag_p1_tracked |
321 315 dirstate_flag_p2_tracked | dirstate_flag_merged);
322 316 } else if (size == dirstate_v1_from_p2) {
323 317 t->flags =
324 318 (dirstate_flag_p2_tracked | dirstate_flag_clean_p2);
325 319 } else {
326 320 t->flags = dirstate_flag_p1_tracked;
327 321 }
328 322 } else if (state == 'n') {
329 323 if (size == dirstate_v1_from_p2) {
330 324 t->flags =
331 325 (dirstate_flag_wc_tracked |
332 326 dirstate_flag_p2_tracked | dirstate_flag_clean_p2);
333 327 t->mode = 0;
334 328 t->size = dirstate_v1_from_p2;
335 329 t->mtime = ambiguous_time;
336 330 } else if (size == dirstate_v1_nonnormal) {
337 331 t->flags = (dirstate_flag_wc_tracked |
338 332 dirstate_flag_p1_tracked |
339 333 dirstate_flag_possibly_dirty);
340 334 t->mode = 0;
341 335 t->size = dirstate_v1_nonnormal;
342 336 t->mtime = ambiguous_time;
343 337 } else if (mtime == ambiguous_time) {
344 338 t->flags = (dirstate_flag_wc_tracked |
345 339 dirstate_flag_p1_tracked |
346 340 dirstate_flag_possibly_dirty);
347 341 t->mode = mode;
348 342 t->size = size;
349 343 t->mtime = 0;
350 344 } else {
351 345 t->flags = (dirstate_flag_wc_tracked |
352 346 dirstate_flag_p1_tracked);
353 347 t->mode = mode;
354 348 t->size = size;
355 349 t->mtime = mtime;
356 350 }
357 } else if (state == ' ') {
358 /* XXX Rust is using this special case, it should be clean up
359 * later. */
360 t->flags = dirstate_flag_rust_special;
361 t->mode = mode;
362 t->size = size;
363 t->mtime = mtime;
364 351 } else {
365 352 PyErr_Format(PyExc_RuntimeError,
366 353 "unknown state: `%c` (%d, %d, %d)", state, mode,
367 354 size, mtime, NULL);
368 355 return NULL;
369 356 }
370 357
371 358 return t;
372 359 }
373 360
374 361 /* This will never change since it's bound to V1, unlike `dirstate_item_new` */
375 362 static PyObject *dirstate_item_from_v1_meth(PyTypeObject *subtype,
376 363 PyObject *args)
377 364 {
378 365 /* We do all the initialization here and not a tp_init function because
379 366 * dirstate_item is immutable. */
380 367 char state;
381 368 int size, mode, mtime;
382 369 if (!PyArg_ParseTuple(args, "ciii", &state, &mode, &size, &mtime)) {
383 370 return NULL;
384 371 }
385 372 return (PyObject *)dirstate_item_from_v1_data(state, mode, size, mtime);
386 373 };
387 374
388 375 /* constructor to help legacy API to build a new "added" item
389 376
390 377 Should eventually be removed */
391 378 static PyObject *dirstate_item_new_added(PyTypeObject *subtype)
392 379 {
393 380 dirstateItemObject *t;
394 381 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
395 382 if (!t) {
396 383 return NULL;
397 384 }
398 385 t->flags = dirstate_flag_wc_tracked;
399 386 t->mode = 0;
400 387 t->size = dirstate_v1_nonnormal;
401 388 t->mtime = ambiguous_time;
402 389 return (PyObject *)t;
403 390 };
404 391
405 392 /* constructor to help legacy API to build a new "merged" item
406 393
407 394 Should eventually be removed */
408 395 static PyObject *dirstate_item_new_merged(PyTypeObject *subtype)
409 396 {
410 397 dirstateItemObject *t;
411 398 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
412 399 if (!t) {
413 400 return NULL;
414 401 }
415 402 t->flags = (dirstate_flag_wc_tracked | dirstate_flag_p1_tracked |
416 403 dirstate_flag_p2_tracked | dirstate_flag_merged);
417 404 t->mode = 0;
418 405 t->size = dirstate_v1_from_p2;
419 406 t->mtime = ambiguous_time;
420 407 return (PyObject *)t;
421 408 };
422 409
423 410 /* constructor to help legacy API to build a new "from_p2" item
424 411
425 412 Should eventually be removed */
426 413 static PyObject *dirstate_item_new_from_p2(PyTypeObject *subtype)
427 414 {
428 415 /* We do all the initialization here and not a tp_init function because
429 416 * dirstate_item is immutable. */
430 417 dirstateItemObject *t;
431 418 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
432 419 if (!t) {
433 420 return NULL;
434 421 }
435 422 t->flags = (dirstate_flag_wc_tracked | dirstate_flag_p2_tracked |
436 423 dirstate_flag_clean_p2);
437 424 t->mode = 0;
438 425 t->size = dirstate_v1_from_p2;
439 426 t->mtime = ambiguous_time;
440 427 return (PyObject *)t;
441 428 };
442 429
443 430 /* constructor to help legacy API to build a new "possibly" item
444 431
445 432 Should eventually be removed */
446 433 static PyObject *dirstate_item_new_possibly_dirty(PyTypeObject *subtype)
447 434 {
448 435 /* We do all the initialization here and not a tp_init function because
449 436 * dirstate_item is immutable. */
450 437 dirstateItemObject *t;
451 438 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
452 439 if (!t) {
453 440 return NULL;
454 441 }
455 442 t->flags = (dirstate_flag_wc_tracked | dirstate_flag_p1_tracked |
456 443 dirstate_flag_possibly_dirty);
457 444 t->mode = 0;
458 445 t->size = dirstate_v1_nonnormal;
459 446 t->mtime = ambiguous_time;
460 447 return (PyObject *)t;
461 448 };
462 449
463 450 /* constructor to help legacy API to build a new "normal" item
464 451
465 452 Should eventually be removed */
466 453 static PyObject *dirstate_item_new_normal(PyTypeObject *subtype, PyObject *args)
467 454 {
468 455 /* We do all the initialization here and not a tp_init function because
469 456 * dirstate_item is immutable. */
470 457 dirstateItemObject *t;
471 458 int size, mode, mtime;
472 459 if (!PyArg_ParseTuple(args, "iii", &mode, &size, &mtime)) {
473 460 return NULL;
474 461 }
475 462
476 463 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
477 464 if (!t) {
478 465 return NULL;
479 466 }
480 467 t->flags = (dirstate_flag_wc_tracked | dirstate_flag_p1_tracked);
481 468 t->mode = mode;
482 469 t->size = size;
483 470 t->mtime = mtime;
484 471 return (PyObject *)t;
485 472 };
486 473
487 474 /* This means the next status call will have to actually check its content
488 475 to make sure it is correct. */
489 476 static PyObject *dirstate_item_set_possibly_dirty(dirstateItemObject *self)
490 477 {
491 478 self->flags |= dirstate_flag_possibly_dirty;
492 479 Py_RETURN_NONE;
493 480 }
494 481
495 482 /* See docstring of the python implementation for details */
496 483 static PyObject *dirstate_item_set_clean(dirstateItemObject *self,
497 484 PyObject *args)
498 485 {
499 486 int size, mode, mtime;
500 487 if (!PyArg_ParseTuple(args, "iii", &mode, &size, &mtime)) {
501 488 return NULL;
502 489 }
503 490 self->flags = dirstate_flag_wc_tracked | dirstate_flag_p1_tracked;
504 491 self->mode = mode;
505 492 self->size = size;
506 493 self->mtime = mtime;
507 494 Py_RETURN_NONE;
508 495 }
509 496
510 497 static PyObject *dirstate_item_set_tracked(dirstateItemObject *self)
511 498 {
512 499 self->flags |= dirstate_flag_wc_tracked;
513 500 self->flags |= dirstate_flag_possibly_dirty;
514 501 /* size = None on the python size turn into size = NON_NORMAL when
515 502 * accessed. So the next line is currently required, but a some future
516 503 * clean up would be welcome. */
517 504 self->size = dirstate_v1_nonnormal;
518 505 Py_RETURN_NONE;
519 506 }
520 507
521 508 static PyObject *dirstate_item_set_untracked(dirstateItemObject *self)
522 509 {
523 510 self->flags &= ~dirstate_flag_wc_tracked;
524 511 self->mode = 0;
525 512 self->mtime = 0;
526 513 self->size = 0;
527 514 Py_RETURN_NONE;
528 515 }
529 516
530 517 static PyMethodDef dirstate_item_methods[] = {
531 518 {"v1_state", (PyCFunction)dirstate_item_v1_state, METH_NOARGS,
532 519 "return a \"state\" suitable for v1 serialization"},
533 520 {"v1_mode", (PyCFunction)dirstate_item_v1_mode, METH_NOARGS,
534 521 "return a \"mode\" suitable for v1 serialization"},
535 522 {"v1_size", (PyCFunction)dirstate_item_v1_size, METH_NOARGS,
536 523 "return a \"size\" suitable for v1 serialization"},
537 524 {"v1_mtime", (PyCFunction)dirstate_item_v1_mtime, METH_NOARGS,
538 525 "return a \"mtime\" suitable for v1 serialization"},
539 526 {"need_delay", (PyCFunction)dirstate_item_need_delay, METH_O,
540 527 "True if the stored mtime would be ambiguous with the current time"},
541 528 {"from_v1_data", (PyCFunction)dirstate_item_from_v1_meth,
542 529 METH_VARARGS | METH_CLASS, "build a new DirstateItem object from V1 data"},
543 530 {"new_added", (PyCFunction)dirstate_item_new_added,
544 531 METH_NOARGS | METH_CLASS,
545 532 "constructor to help legacy API to build a new \"added\" item"},
546 533 {"new_merged", (PyCFunction)dirstate_item_new_merged,
547 534 METH_NOARGS | METH_CLASS,
548 535 "constructor to help legacy API to build a new \"merged\" item"},
549 536 {"new_from_p2", (PyCFunction)dirstate_item_new_from_p2,
550 537 METH_NOARGS | METH_CLASS,
551 538 "constructor to help legacy API to build a new \"from_p2\" item"},
552 539 {"new_possibly_dirty", (PyCFunction)dirstate_item_new_possibly_dirty,
553 540 METH_NOARGS | METH_CLASS,
554 541 "constructor to help legacy API to build a new \"possibly_dirty\" item"},
555 542 {"new_normal", (PyCFunction)dirstate_item_new_normal,
556 543 METH_VARARGS | METH_CLASS,
557 544 "constructor to help legacy API to build a new \"normal\" item"},
558 545 {"set_possibly_dirty", (PyCFunction)dirstate_item_set_possibly_dirty,
559 546 METH_NOARGS, "mark a file as \"possibly dirty\""},
560 547 {"set_clean", (PyCFunction)dirstate_item_set_clean, METH_VARARGS,
561 548 "mark a file as \"clean\""},
562 549 {"set_tracked", (PyCFunction)dirstate_item_set_tracked, METH_NOARGS,
563 550 "mark a file as \"tracked\""},
564 551 {"set_untracked", (PyCFunction)dirstate_item_set_untracked, METH_NOARGS,
565 552 "mark a file as \"untracked\""},
566 553 {NULL} /* Sentinel */
567 554 };
568 555
569 556 static PyObject *dirstate_item_get_mode(dirstateItemObject *self)
570 557 {
571 558 return PyInt_FromLong(dirstate_item_c_v1_mode(self));
572 559 };
573 560
574 561 static PyObject *dirstate_item_get_size(dirstateItemObject *self)
575 562 {
576 563 return PyInt_FromLong(dirstate_item_c_v1_size(self));
577 564 };
578 565
579 566 static PyObject *dirstate_item_get_mtime(dirstateItemObject *self)
580 567 {
581 568 return PyInt_FromLong(dirstate_item_c_v1_mtime(self));
582 569 };
583 570
584 571 static PyObject *dirstate_item_get_state(dirstateItemObject *self)
585 572 {
586 573 char state = dirstate_item_c_v1_state(self);
587 574 return PyBytes_FromStringAndSize(&state, 1);
588 575 };
589 576
590 577 static PyObject *dirstate_item_get_tracked(dirstateItemObject *self)
591 578 {
592 579 if (dirstate_item_c_tracked(self)) {
593 580 Py_RETURN_TRUE;
594 581 } else {
595 582 Py_RETURN_FALSE;
596 583 }
597 584 };
598 585
599 586 static PyObject *dirstate_item_get_added(dirstateItemObject *self)
600 587 {
601 588 if (dirstate_item_c_added(self)) {
602 589 Py_RETURN_TRUE;
603 590 } else {
604 591 Py_RETURN_FALSE;
605 592 }
606 593 };
607 594
608 595 static PyObject *dirstate_item_get_merged(dirstateItemObject *self)
609 596 {
610 597 if (dirstate_item_c_merged(self)) {
611 598 Py_RETURN_TRUE;
612 599 } else {
613 600 Py_RETURN_FALSE;
614 601 }
615 602 };
616 603
617 604 static PyObject *dirstate_item_get_merged_removed(dirstateItemObject *self)
618 605 {
619 606 if (dirstate_item_c_merged_removed(self)) {
620 607 Py_RETURN_TRUE;
621 608 } else {
622 609 Py_RETURN_FALSE;
623 610 }
624 611 };
625 612
626 613 static PyObject *dirstate_item_get_from_p2(dirstateItemObject *self)
627 614 {
628 615 if (dirstate_item_c_from_p2(self)) {
629 616 Py_RETURN_TRUE;
630 617 } else {
631 618 Py_RETURN_FALSE;
632 619 }
633 620 };
634 621
635 622 static PyObject *dirstate_item_get_from_p2_removed(dirstateItemObject *self)
636 623 {
637 624 if (dirstate_item_c_from_p2_removed(self)) {
638 625 Py_RETURN_TRUE;
639 626 } else {
640 627 Py_RETURN_FALSE;
641 628 }
642 629 };
643 630
644 631 static PyObject *dirstate_item_get_removed(dirstateItemObject *self)
645 632 {
646 633 if (dirstate_item_c_removed(self)) {
647 634 Py_RETURN_TRUE;
648 635 } else {
649 636 Py_RETURN_FALSE;
650 637 }
651 638 };
652 639
653 640 static PyObject *dm_nonnormal(dirstateItemObject *self)
654 641 {
655 642 if ((dirstate_item_c_v1_state(self) != 'n') ||
656 643 (dirstate_item_c_v1_mtime(self) == ambiguous_time)) {
657 644 Py_RETURN_TRUE;
658 645 } else {
659 646 Py_RETURN_FALSE;
660 647 }
661 648 };
662 649 static PyObject *dm_otherparent(dirstateItemObject *self)
663 650 {
664 651 if (dirstate_item_c_v1_mtime(self) == dirstate_v1_from_p2) {
665 652 Py_RETURN_TRUE;
666 653 } else {
667 654 Py_RETURN_FALSE;
668 655 }
669 656 };
670 657
671 658 static PyGetSetDef dirstate_item_getset[] = {
672 659 {"mode", (getter)dirstate_item_get_mode, NULL, "mode", NULL},
673 660 {"size", (getter)dirstate_item_get_size, NULL, "size", NULL},
674 661 {"mtime", (getter)dirstate_item_get_mtime, NULL, "mtime", NULL},
675 662 {"state", (getter)dirstate_item_get_state, NULL, "state", NULL},
676 663 {"tracked", (getter)dirstate_item_get_tracked, NULL, "tracked", NULL},
677 664 {"added", (getter)dirstate_item_get_added, NULL, "added", NULL},
678 665 {"merged_removed", (getter)dirstate_item_get_merged_removed, NULL,
679 666 "merged_removed", NULL},
680 667 {"merged", (getter)dirstate_item_get_merged, NULL, "merged", NULL},
681 668 {"from_p2_removed", (getter)dirstate_item_get_from_p2_removed, NULL,
682 669 "from_p2_removed", NULL},
683 670 {"from_p2", (getter)dirstate_item_get_from_p2, NULL, "from_p2", NULL},
684 671 {"removed", (getter)dirstate_item_get_removed, NULL, "removed", NULL},
685 672 {"dm_nonnormal", (getter)dm_nonnormal, NULL, "dm_nonnormal", NULL},
686 673 {"dm_otherparent", (getter)dm_otherparent, NULL, "dm_otherparent", NULL},
687 674 {NULL} /* Sentinel */
688 675 };
689 676
690 677 PyTypeObject dirstateItemType = {
691 678 PyVarObject_HEAD_INIT(NULL, 0) /* header */
692 679 "dirstate_tuple", /* tp_name */
693 680 sizeof(dirstateItemObject), /* tp_basicsize */
694 681 0, /* tp_itemsize */
695 682 (destructor)dirstate_item_dealloc, /* tp_dealloc */
696 683 0, /* tp_print */
697 684 0, /* tp_getattr */
698 685 0, /* tp_setattr */
699 686 0, /* tp_compare */
700 687 0, /* tp_repr */
701 688 0, /* tp_as_number */
702 689 0, /* tp_as_sequence */
703 690 0, /* tp_as_mapping */
704 691 0, /* tp_hash */
705 692 0, /* tp_call */
706 693 0, /* tp_str */
707 694 0, /* tp_getattro */
708 695 0, /* tp_setattro */
709 696 0, /* tp_as_buffer */
710 697 Py_TPFLAGS_DEFAULT, /* tp_flags */
711 698 "dirstate tuple", /* tp_doc */
712 699 0, /* tp_traverse */
713 700 0, /* tp_clear */
714 701 0, /* tp_richcompare */
715 702 0, /* tp_weaklistoffset */
716 703 0, /* tp_iter */
717 704 0, /* tp_iternext */
718 705 dirstate_item_methods, /* tp_methods */
719 706 0, /* tp_members */
720 707 dirstate_item_getset, /* tp_getset */
721 708 0, /* tp_base */
722 709 0, /* tp_dict */
723 710 0, /* tp_descr_get */
724 711 0, /* tp_descr_set */
725 712 0, /* tp_dictoffset */
726 713 0, /* tp_init */
727 714 0, /* tp_alloc */
728 715 dirstate_item_new, /* tp_new */
729 716 };
730 717
731 718 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
732 719 {
733 720 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
734 721 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
735 722 char state, *cur, *str, *cpos;
736 723 int mode, size, mtime;
737 724 unsigned int flen, pos = 40;
738 725 Py_ssize_t len = 40;
739 726 Py_ssize_t readlen;
740 727
741 728 if (!PyArg_ParseTuple(
742 729 args, PY23("O!O!s#:parse_dirstate", "O!O!y#:parse_dirstate"),
743 730 &PyDict_Type, &dmap, &PyDict_Type, &cmap, &str, &readlen)) {
744 731 goto quit;
745 732 }
746 733
747 734 len = readlen;
748 735
749 736 /* read parents */
750 737 if (len < 40) {
751 738 PyErr_SetString(PyExc_ValueError,
752 739 "too little data for parents");
753 740 goto quit;
754 741 }
755 742
756 743 parents = Py_BuildValue(PY23("s#s#", "y#y#"), str, (Py_ssize_t)20,
757 744 str + 20, (Py_ssize_t)20);
758 745 if (!parents) {
759 746 goto quit;
760 747 }
761 748
762 749 /* read filenames */
763 750 while (pos >= 40 && pos < len) {
764 751 if (pos + 17 > len) {
765 752 PyErr_SetString(PyExc_ValueError,
766 753 "overflow in dirstate");
767 754 goto quit;
768 755 }
769 756 cur = str + pos;
770 757 /* unpack header */
771 758 state = *cur;
772 759 mode = getbe32(cur + 1);
773 760 size = getbe32(cur + 5);
774 761 mtime = getbe32(cur + 9);
775 762 flen = getbe32(cur + 13);
776 763 pos += 17;
777 764 cur += 17;
778 765 if (flen > len - pos) {
779 766 PyErr_SetString(PyExc_ValueError,
780 767 "overflow in dirstate");
781 768 goto quit;
782 769 }
783 770
784 771 entry = (PyObject *)dirstate_item_from_v1_data(state, mode,
785 772 size, mtime);
786 773 cpos = memchr(cur, 0, flen);
787 774 if (cpos) {
788 775 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
789 776 cname = PyBytes_FromStringAndSize(
790 777 cpos + 1, flen - (cpos - cur) - 1);
791 778 if (!fname || !cname ||
792 779 PyDict_SetItem(cmap, fname, cname) == -1 ||
793 780 PyDict_SetItem(dmap, fname, entry) == -1) {
794 781 goto quit;
795 782 }
796 783 Py_DECREF(cname);
797 784 } else {
798 785 fname = PyBytes_FromStringAndSize(cur, flen);
799 786 if (!fname ||
800 787 PyDict_SetItem(dmap, fname, entry) == -1) {
801 788 goto quit;
802 789 }
803 790 }
804 791 Py_DECREF(fname);
805 792 Py_DECREF(entry);
806 793 fname = cname = entry = NULL;
807 794 pos += flen;
808 795 }
809 796
810 797 ret = parents;
811 798 Py_INCREF(ret);
812 799 quit:
813 800 Py_XDECREF(fname);
814 801 Py_XDECREF(cname);
815 802 Py_XDECREF(entry);
816 803 Py_XDECREF(parents);
817 804 return ret;
818 805 }
819 806
820 807 /*
821 808 * Build a set of non-normal and other parent entries from the dirstate dmap
822 809 */
823 810 static PyObject *nonnormalotherparententries(PyObject *self, PyObject *args)
824 811 {
825 812 PyObject *dmap, *fname, *v;
826 813 PyObject *nonnset = NULL, *otherpset = NULL, *result = NULL;
827 814 Py_ssize_t pos;
828 815
829 816 if (!PyArg_ParseTuple(args, "O!:nonnormalentries", &PyDict_Type,
830 817 &dmap)) {
831 818 goto bail;
832 819 }
833 820
834 821 nonnset = PySet_New(NULL);
835 822 if (nonnset == NULL) {
836 823 goto bail;
837 824 }
838 825
839 826 otherpset = PySet_New(NULL);
840 827 if (otherpset == NULL) {
841 828 goto bail;
842 829 }
843 830
844 831 pos = 0;
845 832 while (PyDict_Next(dmap, &pos, &fname, &v)) {
846 833 dirstateItemObject *t;
847 834 if (!dirstate_tuple_check(v)) {
848 835 PyErr_SetString(PyExc_TypeError,
849 836 "expected a dirstate tuple");
850 837 goto bail;
851 838 }
852 839 t = (dirstateItemObject *)v;
853 840
854 841 if (dirstate_item_c_from_p2(t)) {
855 842 if (PySet_Add(otherpset, fname) == -1) {
856 843 goto bail;
857 844 }
858 845 }
859 846 if (!(t->flags & dirstate_flag_wc_tracked) ||
860 847 !(t->flags &
861 848 (dirstate_flag_p1_tracked | dirstate_flag_p2_tracked)) ||
862 849 (t->flags &
863 850 (dirstate_flag_possibly_dirty | dirstate_flag_merged))) {
864 851 if (PySet_Add(nonnset, fname) == -1) {
865 852 goto bail;
866 853 }
867 854 }
868 855 }
869 856
870 857 result = Py_BuildValue("(OO)", nonnset, otherpset);
871 858 if (result == NULL) {
872 859 goto bail;
873 860 }
874 861 Py_DECREF(nonnset);
875 862 Py_DECREF(otherpset);
876 863 return result;
877 864 bail:
878 865 Py_XDECREF(nonnset);
879 866 Py_XDECREF(otherpset);
880 867 Py_XDECREF(result);
881 868 return NULL;
882 869 }
883 870
884 871 /*
885 872 * Efficiently pack a dirstate object into its on-disk format.
886 873 */
887 874 static PyObject *pack_dirstate(PyObject *self, PyObject *args)
888 875 {
889 876 PyObject *packobj = NULL;
890 877 PyObject *map, *copymap, *pl, *mtime_unset = NULL;
891 878 Py_ssize_t nbytes, pos, l;
892 879 PyObject *k, *v = NULL, *pn;
893 880 char *p, *s;
894 881 int now;
895 882
896 883 if (!PyArg_ParseTuple(args, "O!O!O!i:pack_dirstate", &PyDict_Type, &map,
897 884 &PyDict_Type, &copymap, &PyTuple_Type, &pl,
898 885 &now)) {
899 886 return NULL;
900 887 }
901 888
902 889 if (PyTuple_Size(pl) != 2) {
903 890 PyErr_SetString(PyExc_TypeError, "expected 2-element tuple");
904 891 return NULL;
905 892 }
906 893
907 894 /* Figure out how much we need to allocate. */
908 895 for (nbytes = 40, pos = 0; PyDict_Next(map, &pos, &k, &v);) {
909 896 PyObject *c;
910 897 if (!PyBytes_Check(k)) {
911 898 PyErr_SetString(PyExc_TypeError, "expected string key");
912 899 goto bail;
913 900 }
914 901 nbytes += PyBytes_GET_SIZE(k) + 17;
915 902 c = PyDict_GetItem(copymap, k);
916 903 if (c) {
917 904 if (!PyBytes_Check(c)) {
918 905 PyErr_SetString(PyExc_TypeError,
919 906 "expected string key");
920 907 goto bail;
921 908 }
922 909 nbytes += PyBytes_GET_SIZE(c) + 1;
923 910 }
924 911 }
925 912
926 913 packobj = PyBytes_FromStringAndSize(NULL, nbytes);
927 914 if (packobj == NULL) {
928 915 goto bail;
929 916 }
930 917
931 918 p = PyBytes_AS_STRING(packobj);
932 919
933 920 pn = PyTuple_GET_ITEM(pl, 0);
934 921 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
935 922 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
936 923 goto bail;
937 924 }
938 925 memcpy(p, s, l);
939 926 p += 20;
940 927 pn = PyTuple_GET_ITEM(pl, 1);
941 928 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
942 929 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
943 930 goto bail;
944 931 }
945 932 memcpy(p, s, l);
946 933 p += 20;
947 934
948 935 for (pos = 0; PyDict_Next(map, &pos, &k, &v);) {
949 936 dirstateItemObject *tuple;
950 937 char state;
951 938 int mode, size, mtime;
952 939 Py_ssize_t len, l;
953 940 PyObject *o;
954 941 char *t;
955 942
956 943 if (!dirstate_tuple_check(v)) {
957 944 PyErr_SetString(PyExc_TypeError,
958 945 "expected a dirstate tuple");
959 946 goto bail;
960 947 }
961 948 tuple = (dirstateItemObject *)v;
962 949
963 950 state = dirstate_item_c_v1_state(tuple);
964 951 mode = dirstate_item_c_v1_mode(tuple);
965 952 size = dirstate_item_c_v1_size(tuple);
966 953 mtime = dirstate_item_c_v1_mtime(tuple);
967 954 if (state == 'n' && mtime == now) {
968 955 /* See pure/parsers.py:pack_dirstate for why we do
969 956 * this. */
970 957 mtime = -1;
971 958 mtime_unset = (PyObject *)dirstate_item_from_v1_data(
972 959 state, mode, size, mtime);
973 960 if (!mtime_unset) {
974 961 goto bail;
975 962 }
976 963 if (PyDict_SetItem(map, k, mtime_unset) == -1) {
977 964 goto bail;
978 965 }
979 966 Py_DECREF(mtime_unset);
980 967 mtime_unset = NULL;
981 968 }
982 969 *p++ = state;
983 970 putbe32((uint32_t)mode, p);
984 971 putbe32((uint32_t)size, p + 4);
985 972 putbe32((uint32_t)mtime, p + 8);
986 973 t = p + 12;
987 974 p += 16;
988 975 len = PyBytes_GET_SIZE(k);
989 976 memcpy(p, PyBytes_AS_STRING(k), len);
990 977 p += len;
991 978 o = PyDict_GetItem(copymap, k);
992 979 if (o) {
993 980 *p++ = '\0';
994 981 l = PyBytes_GET_SIZE(o);
995 982 memcpy(p, PyBytes_AS_STRING(o), l);
996 983 p += l;
997 984 len += l + 1;
998 985 }
999 986 putbe32((uint32_t)len, t);
1000 987 }
1001 988
1002 989 pos = p - PyBytes_AS_STRING(packobj);
1003 990 if (pos != nbytes) {
1004 991 PyErr_Format(PyExc_SystemError, "bad dirstate size: %ld != %ld",
1005 992 (long)pos, (long)nbytes);
1006 993 goto bail;
1007 994 }
1008 995
1009 996 return packobj;
1010 997 bail:
1011 998 Py_XDECREF(mtime_unset);
1012 999 Py_XDECREF(packobj);
1013 1000 Py_XDECREF(v);
1014 1001 return NULL;
1015 1002 }
1016 1003
1017 1004 #define BUMPED_FIX 1
1018 1005 #define USING_SHA_256 2
1019 1006 #define FM1_HEADER_SIZE (4 + 8 + 2 + 2 + 1 + 1 + 1)
1020 1007
1021 1008 static PyObject *readshas(const char *source, unsigned char num,
1022 1009 Py_ssize_t hashwidth)
1023 1010 {
1024 1011 int i;
1025 1012 PyObject *list = PyTuple_New(num);
1026 1013 if (list == NULL) {
1027 1014 return NULL;
1028 1015 }
1029 1016 for (i = 0; i < num; i++) {
1030 1017 PyObject *hash = PyBytes_FromStringAndSize(source, hashwidth);
1031 1018 if (hash == NULL) {
1032 1019 Py_DECREF(list);
1033 1020 return NULL;
1034 1021 }
1035 1022 PyTuple_SET_ITEM(list, i, hash);
1036 1023 source += hashwidth;
1037 1024 }
1038 1025 return list;
1039 1026 }
1040 1027
1041 1028 static PyObject *fm1readmarker(const char *databegin, const char *dataend,
1042 1029 uint32_t *msize)
1043 1030 {
1044 1031 const char *data = databegin;
1045 1032 const char *meta;
1046 1033
1047 1034 double mtime;
1048 1035 int16_t tz;
1049 1036 uint16_t flags;
1050 1037 unsigned char nsuccs, nparents, nmetadata;
1051 1038 Py_ssize_t hashwidth = 20;
1052 1039
1053 1040 PyObject *prec = NULL, *parents = NULL, *succs = NULL;
1054 1041 PyObject *metadata = NULL, *ret = NULL;
1055 1042 int i;
1056 1043
1057 1044 if (data + FM1_HEADER_SIZE > dataend) {
1058 1045 goto overflow;
1059 1046 }
1060 1047
1061 1048 *msize = getbe32(data);
1062 1049 data += 4;
1063 1050 mtime = getbefloat64(data);
1064 1051 data += 8;
1065 1052 tz = getbeint16(data);
1066 1053 data += 2;
1067 1054 flags = getbeuint16(data);
1068 1055 data += 2;
1069 1056
1070 1057 if (flags & USING_SHA_256) {
1071 1058 hashwidth = 32;
1072 1059 }
1073 1060
1074 1061 nsuccs = (unsigned char)(*data++);
1075 1062 nparents = (unsigned char)(*data++);
1076 1063 nmetadata = (unsigned char)(*data++);
1077 1064
1078 1065 if (databegin + *msize > dataend) {
1079 1066 goto overflow;
1080 1067 }
1081 1068 dataend = databegin + *msize; /* narrow down to marker size */
1082 1069
1083 1070 if (data + hashwidth > dataend) {
1084 1071 goto overflow;
1085 1072 }
1086 1073 prec = PyBytes_FromStringAndSize(data, hashwidth);
1087 1074 data += hashwidth;
1088 1075 if (prec == NULL) {
1089 1076 goto bail;
1090 1077 }
1091 1078
1092 1079 if (data + nsuccs * hashwidth > dataend) {
1093 1080 goto overflow;
1094 1081 }
1095 1082 succs = readshas(data, nsuccs, hashwidth);
1096 1083 if (succs == NULL) {
1097 1084 goto bail;
1098 1085 }
1099 1086 data += nsuccs * hashwidth;
1100 1087
1101 1088 if (nparents == 1 || nparents == 2) {
1102 1089 if (data + nparents * hashwidth > dataend) {
1103 1090 goto overflow;
1104 1091 }
1105 1092 parents = readshas(data, nparents, hashwidth);
1106 1093 if (parents == NULL) {
1107 1094 goto bail;
1108 1095 }
1109 1096 data += nparents * hashwidth;
1110 1097 } else {
1111 1098 parents = Py_None;
1112 1099 Py_INCREF(parents);
1113 1100 }
1114 1101
1115 1102 if (data + 2 * nmetadata > dataend) {
1116 1103 goto overflow;
1117 1104 }
1118 1105 meta = data + (2 * nmetadata);
1119 1106 metadata = PyTuple_New(nmetadata);
1120 1107 if (metadata == NULL) {
1121 1108 goto bail;
1122 1109 }
1123 1110 for (i = 0; i < nmetadata; i++) {
1124 1111 PyObject *tmp, *left = NULL, *right = NULL;
1125 1112 Py_ssize_t leftsize = (unsigned char)(*data++);
1126 1113 Py_ssize_t rightsize = (unsigned char)(*data++);
1127 1114 if (meta + leftsize + rightsize > dataend) {
1128 1115 goto overflow;
1129 1116 }
1130 1117 left = PyBytes_FromStringAndSize(meta, leftsize);
1131 1118 meta += leftsize;
1132 1119 right = PyBytes_FromStringAndSize(meta, rightsize);
1133 1120 meta += rightsize;
1134 1121 tmp = PyTuple_New(2);
1135 1122 if (!left || !right || !tmp) {
1136 1123 Py_XDECREF(left);
1137 1124 Py_XDECREF(right);
1138 1125 Py_XDECREF(tmp);
1139 1126 goto bail;
1140 1127 }
1141 1128 PyTuple_SET_ITEM(tmp, 0, left);
1142 1129 PyTuple_SET_ITEM(tmp, 1, right);
1143 1130 PyTuple_SET_ITEM(metadata, i, tmp);
1144 1131 }
1145 1132 ret = Py_BuildValue("(OOHO(di)O)", prec, succs, flags, metadata, mtime,
1146 1133 (int)tz * 60, parents);
1147 1134 goto bail; /* return successfully */
1148 1135
1149 1136 overflow:
1150 1137 PyErr_SetString(PyExc_ValueError, "overflow in obsstore");
1151 1138 bail:
1152 1139 Py_XDECREF(prec);
1153 1140 Py_XDECREF(succs);
1154 1141 Py_XDECREF(metadata);
1155 1142 Py_XDECREF(parents);
1156 1143 return ret;
1157 1144 }
1158 1145
1159 1146 static PyObject *fm1readmarkers(PyObject *self, PyObject *args)
1160 1147 {
1161 1148 const char *data, *dataend;
1162 1149 Py_ssize_t datalen, offset, stop;
1163 1150 PyObject *markers = NULL;
1164 1151
1165 1152 if (!PyArg_ParseTuple(args, PY23("s#nn", "y#nn"), &data, &datalen,
1166 1153 &offset, &stop)) {
1167 1154 return NULL;
1168 1155 }
1169 1156 if (offset < 0) {
1170 1157 PyErr_SetString(PyExc_ValueError,
1171 1158 "invalid negative offset in fm1readmarkers");
1172 1159 return NULL;
1173 1160 }
1174 1161 if (stop > datalen) {
1175 1162 PyErr_SetString(
1176 1163 PyExc_ValueError,
1177 1164 "stop longer than data length in fm1readmarkers");
1178 1165 return NULL;
1179 1166 }
1180 1167 dataend = data + datalen;
1181 1168 data += offset;
1182 1169 markers = PyList_New(0);
1183 1170 if (!markers) {
1184 1171 return NULL;
1185 1172 }
1186 1173 while (offset < stop) {
1187 1174 uint32_t msize;
1188 1175 int error;
1189 1176 PyObject *record = fm1readmarker(data, dataend, &msize);
1190 1177 if (!record) {
1191 1178 goto bail;
1192 1179 }
1193 1180 error = PyList_Append(markers, record);
1194 1181 Py_DECREF(record);
1195 1182 if (error) {
1196 1183 goto bail;
1197 1184 }
1198 1185 data += msize;
1199 1186 offset += msize;
1200 1187 }
1201 1188 return markers;
1202 1189 bail:
1203 1190 Py_DECREF(markers);
1204 1191 return NULL;
1205 1192 }
1206 1193
1207 1194 static char parsers_doc[] = "Efficient content parsing.";
1208 1195
1209 1196 PyObject *encodedir(PyObject *self, PyObject *args);
1210 1197 PyObject *pathencode(PyObject *self, PyObject *args);
1211 1198 PyObject *lowerencode(PyObject *self, PyObject *args);
1212 1199 PyObject *parse_index2(PyObject *self, PyObject *args, PyObject *kwargs);
1213 1200
1214 1201 static PyMethodDef methods[] = {
1215 1202 {"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"},
1216 1203 {"nonnormalotherparententries", nonnormalotherparententries, METH_VARARGS,
1217 1204 "create a set containing non-normal and other parent entries of given "
1218 1205 "dirstate\n"},
1219 1206 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
1220 1207 {"parse_index2", (PyCFunction)parse_index2, METH_VARARGS | METH_KEYWORDS,
1221 1208 "parse a revlog index\n"},
1222 1209 {"isasciistr", isasciistr, METH_VARARGS, "check if an ASCII string\n"},
1223 1210 {"asciilower", asciilower, METH_VARARGS, "lowercase an ASCII string\n"},
1224 1211 {"asciiupper", asciiupper, METH_VARARGS, "uppercase an ASCII string\n"},
1225 1212 {"dict_new_presized", dict_new_presized, METH_VARARGS,
1226 1213 "construct a dict with an expected size\n"},
1227 1214 {"make_file_foldmap", make_file_foldmap, METH_VARARGS,
1228 1215 "make file foldmap\n"},
1229 1216 {"jsonescapeu8fast", jsonescapeu8fast, METH_VARARGS,
1230 1217 "escape a UTF-8 byte string to JSON (fast path)\n"},
1231 1218 {"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"},
1232 1219 {"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"},
1233 1220 {"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"},
1234 1221 {"fm1readmarkers", fm1readmarkers, METH_VARARGS,
1235 1222 "parse v1 obsolete markers\n"},
1236 1223 {NULL, NULL}};
1237 1224
1238 1225 void dirs_module_init(PyObject *mod);
1239 1226 void manifest_module_init(PyObject *mod);
1240 1227 void revlog_module_init(PyObject *mod);
1241 1228
1242 1229 static const int version = 20;
1243 1230
1244 1231 static void module_init(PyObject *mod)
1245 1232 {
1246 1233 PyObject *capsule = NULL;
1247 1234 PyModule_AddIntConstant(mod, "version", version);
1248 1235
1249 1236 /* This module constant has two purposes. First, it lets us unit test
1250 1237 * the ImportError raised without hard-coding any error text. This
1251 1238 * means we can change the text in the future without breaking tests,
1252 1239 * even across changesets without a recompile. Second, its presence
1253 1240 * can be used to determine whether the version-checking logic is
1254 1241 * present, which also helps in testing across changesets without a
1255 1242 * recompile. Note that this means the pure-Python version of parsers
1256 1243 * should not have this module constant. */
1257 1244 PyModule_AddStringConstant(mod, "versionerrortext", versionerrortext);
1258 1245
1259 1246 dirs_module_init(mod);
1260 1247 manifest_module_init(mod);
1261 1248 revlog_module_init(mod);
1262 1249
1263 1250 capsule = PyCapsule_New(
1264 1251 dirstate_item_from_v1_data,
1265 1252 "mercurial.cext.parsers.make_dirstate_item_CAPI", NULL);
1266 1253 if (capsule != NULL)
1267 1254 PyModule_AddObject(mod, "make_dirstate_item_CAPI", capsule);
1268 1255
1269 1256 if (PyType_Ready(&dirstateItemType) < 0) {
1270 1257 return;
1271 1258 }
1272 1259 Py_INCREF(&dirstateItemType);
1273 1260 PyModule_AddObject(mod, "DirstateItem", (PyObject *)&dirstateItemType);
1274 1261 }
1275 1262
1276 1263 static int check_python_version(void)
1277 1264 {
1278 1265 PyObject *sys = PyImport_ImportModule("sys"), *ver;
1279 1266 long hexversion;
1280 1267 if (!sys) {
1281 1268 return -1;
1282 1269 }
1283 1270 ver = PyObject_GetAttrString(sys, "hexversion");
1284 1271 Py_DECREF(sys);
1285 1272 if (!ver) {
1286 1273 return -1;
1287 1274 }
1288 1275 hexversion = PyInt_AsLong(ver);
1289 1276 Py_DECREF(ver);
1290 1277 /* sys.hexversion is a 32-bit number by default, so the -1 case
1291 1278 * should only occur in unusual circumstances (e.g. if sys.hexversion
1292 1279 * is manually set to an invalid value). */
1293 1280 if ((hexversion == -1) || (hexversion >> 16 != PY_VERSION_HEX >> 16)) {
1294 1281 PyErr_Format(PyExc_ImportError,
1295 1282 "%s: The Mercurial extension "
1296 1283 "modules were compiled with Python " PY_VERSION
1297 1284 ", but "
1298 1285 "Mercurial is currently using Python with "
1299 1286 "sys.hexversion=%ld: "
1300 1287 "Python %s\n at: %s",
1301 1288 versionerrortext, hexversion, Py_GetVersion(),
1302 1289 Py_GetProgramFullPath());
1303 1290 return -1;
1304 1291 }
1305 1292 return 0;
1306 1293 }
1307 1294
1308 1295 #ifdef IS_PY3K
1309 1296 static struct PyModuleDef parsers_module = {PyModuleDef_HEAD_INIT, "parsers",
1310 1297 parsers_doc, -1, methods};
1311 1298
1312 1299 PyMODINIT_FUNC PyInit_parsers(void)
1313 1300 {
1314 1301 PyObject *mod;
1315 1302
1316 1303 if (check_python_version() == -1)
1317 1304 return NULL;
1318 1305 mod = PyModule_Create(&parsers_module);
1319 1306 module_init(mod);
1320 1307 return mod;
1321 1308 }
1322 1309 #else
1323 1310 PyMODINIT_FUNC initparsers(void)
1324 1311 {
1325 1312 PyObject *mod;
1326 1313
1327 1314 if (check_python_version() == -1) {
1328 1315 return;
1329 1316 }
1330 1317 mod = Py_InitModule3("parsers", methods, parsers_doc);
1331 1318 module_init(mod);
1332 1319 }
1333 1320 #endif
@@ -1,83 +1,82 b''
1 1 /*
2 2 util.h - utility functions for interfacing with the various python APIs.
3 3
4 4 This software may be used and distributed according to the terms of
5 5 the GNU General Public License, incorporated herein by reference.
6 6 */
7 7
8 8 #ifndef _HG_UTIL_H_
9 9 #define _HG_UTIL_H_
10 10
11 11 #include "compat.h"
12 12
13 13 #if PY_MAJOR_VERSION >= 3
14 14 #define IS_PY3K
15 15 #endif
16 16
17 17 /* helper to switch things like string literal depending on Python version */
18 18 #ifdef IS_PY3K
19 19 #define PY23(py2, py3) py3
20 20 #else
21 21 #define PY23(py2, py3) py2
22 22 #endif
23 23
24 24 /* clang-format off */
25 25 typedef struct {
26 26 PyObject_HEAD
27 27 unsigned char flags;
28 28 int mode;
29 29 int size;
30 30 int mtime;
31 31 } dirstateItemObject;
32 32 /* clang-format on */
33 33
34 34 static const unsigned char dirstate_flag_wc_tracked = 1;
35 35 static const unsigned char dirstate_flag_p1_tracked = 1 << 1;
36 36 static const unsigned char dirstate_flag_p2_tracked = 1 << 2;
37 37 static const unsigned char dirstate_flag_possibly_dirty = 1 << 3;
38 38 static const unsigned char dirstate_flag_merged = 1 << 4;
39 39 static const unsigned char dirstate_flag_clean_p1 = 1 << 5;
40 40 static const unsigned char dirstate_flag_clean_p2 = 1 << 6;
41 static const unsigned char dirstate_flag_rust_special = 1 << 7;
42 41
43 42 extern PyTypeObject dirstateItemType;
44 43 #define dirstate_tuple_check(op) (Py_TYPE(op) == &dirstateItemType)
45 44
46 45 #ifndef MIN
47 46 #define MIN(a, b) (((a) < (b)) ? (a) : (b))
48 47 #endif
49 48 /* VC9 doesn't include bool and lacks stdbool.h based on my searching */
50 49 #if defined(_MSC_VER) || __STDC_VERSION__ < 199901L
51 50 #define true 1
52 51 #define false 0
53 52 typedef unsigned char bool;
54 53 #else
55 54 #include <stdbool.h>
56 55 #endif
57 56
58 57 static inline PyObject *_dict_new_presized(Py_ssize_t expected_size)
59 58 {
60 59 /* _PyDict_NewPresized expects a minused parameter, but it actually
61 60 creates a dictionary that's the nearest power of two bigger than the
62 61 parameter. For example, with the initial minused = 1000, the
63 62 dictionary created has size 1024. Of course in a lot of cases that
64 63 can be greater than the maximum load factor Python's dict object
65 64 expects (= 2/3), so as soon as we cross the threshold we'll resize
66 65 anyway. So create a dictionary that's at least 3/2 the size. */
67 66 return _PyDict_NewPresized(((1 + expected_size) / 2) * 3);
68 67 }
69 68
70 69 /* Convert a PyInt or PyLong to a long. Returns false if there is an
71 70 error, in which case an exception will already have been set. */
72 71 static inline bool pylong_to_long(PyObject *pylong, long *out)
73 72 {
74 73 *out = PyLong_AsLong(pylong);
75 74 /* Fast path to avoid hitting PyErr_Occurred if the value was obviously
76 75 * not an error. */
77 76 if (*out != -1) {
78 77 return true;
79 78 }
80 79 return PyErr_Occurred() == NULL;
81 80 }
82 81
83 82 #endif /* _HG_UTIL_H_ */
@@ -1,122 +1,112 b''
1 1 // dirstate.rs
2 2 //
3 3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 4 //
5 5 // This software may be used and distributed according to the terms of the
6 6 // GNU General Public License version 2 or any later version.
7 7
8 8 //! Bindings for the `hg::dirstate` module provided by the
9 9 //! `hg-core` package.
10 10 //!
11 11 //! From Python, this will be seen as `mercurial.rustext.dirstate`
12 12 mod copymap;
13 13 mod dirs_multiset;
14 14 mod dirstate_map;
15 15 mod non_normal_entries;
16 16 mod status;
17 17 use crate::{
18 18 dirstate::{
19 19 dirs_multiset::Dirs, dirstate_map::DirstateMap, status::status_wrapper,
20 20 },
21 21 exceptions,
22 22 };
23 23 use cpython::{
24 24 PyBytes, PyDict, PyErr, PyList, PyModule, PyObject, PyResult, Python,
25 25 };
26 26 use hg::dirstate_tree::on_disk::V2_FORMAT_MARKER;
27 27 use hg::DirstateEntry;
28 28 use libc::{c_char, c_int};
29 29
30 30 // C code uses a custom `dirstate_tuple` type, checks in multiple instances
31 31 // for this type, and raises a Python `Exception` if the check does not pass.
32 32 // Because this type differs only in name from the regular Python tuple, it
33 33 // would be a good idea in the near future to remove it entirely to allow
34 34 // for a pure Python tuple of the same effective structure to be used,
35 35 // rendering this type and the capsule below useless.
36 36 py_capsule_fn!(
37 37 from mercurial.cext.parsers import make_dirstate_item_CAPI
38 38 as make_dirstate_item_capi
39 39 signature (
40 40 state: c_char,
41 41 mode: c_int,
42 42 size: c_int,
43 43 mtime: c_int,
44 44 ) -> *mut RawPyObject
45 45 );
46 46
47 47 pub fn make_dirstate_item(
48 48 py: Python,
49 49 entry: &DirstateEntry,
50 50 ) -> PyResult<PyObject> {
51 51 // Explicitly go through u8 first, then cast to platform-specific `c_char`
52 52 // because Into<u8> has a specific implementation while `as c_char` would
53 53 // just do a naive enum cast.
54 54 let state_code: u8 = entry.state().into();
55 make_dirstate_item_raw(
56 py,
57 state_code,
58 entry.mode(),
59 entry.size(),
60 entry.mtime(),
61 )
62 }
63 55
64 pub fn make_dirstate_item_raw(
65 py: Python,
66 state: u8,
67 mode: i32,
68 size: i32,
69 mtime: i32,
70 ) -> PyResult<PyObject> {
71 56 let make = make_dirstate_item_capi::retrieve(py)?;
72 57 let maybe_obj = unsafe {
73 let ptr = make(state as c_char, mode, size, mtime);
58 let ptr = make(
59 state_code as c_char,
60 entry.mode(),
61 entry.size(),
62 entry.mtime(),
63 );
74 64 PyObject::from_owned_ptr_opt(py, ptr)
75 65 };
76 66 maybe_obj.ok_or_else(|| PyErr::fetch(py))
77 67 }
78 68
79 69 /// Create the module, with `__package__` given from parent
80 70 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
81 71 let dotted_name = &format!("{}.dirstate", package);
82 72 let m = PyModule::new(py, dotted_name)?;
83 73
84 74 env_logger::init();
85 75
86 76 m.add(py, "__package__", package)?;
87 77 m.add(py, "__doc__", "Dirstate - Rust implementation")?;
88 78
89 79 m.add(
90 80 py,
91 81 "FallbackError",
92 82 py.get_type::<exceptions::FallbackError>(),
93 83 )?;
94 84 m.add_class::<Dirs>(py)?;
95 85 m.add_class::<DirstateMap>(py)?;
96 86 m.add(py, "V2_FORMAT_MARKER", PyBytes::new(py, V2_FORMAT_MARKER))?;
97 87 m.add(
98 88 py,
99 89 "status",
100 90 py_fn!(
101 91 py,
102 92 status_wrapper(
103 93 dmap: DirstateMap,
104 94 root_dir: PyObject,
105 95 matcher: PyObject,
106 96 ignorefiles: PyList,
107 97 check_exec: bool,
108 98 last_normal_time: i64,
109 99 list_clean: bool,
110 100 list_ignored: bool,
111 101 list_unknown: bool,
112 102 collect_traversed_dirs: bool
113 103 )
114 104 ),
115 105 )?;
116 106
117 107 let sys = PyModule::import(py, "sys")?;
118 108 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
119 109 sys_modules.set_item(py, dotted_name, &m)?;
120 110
121 111 Ok(m)
122 112 }
General Comments 0
You need to be logged in to leave comments. Login now