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