##// END OF EJS Templates
revlog: remove unused `rank` variable from D12209...
Martin von Zweigbergk -
r49803:4bb04a50 default
parent child Browse files
Show More
@@ -1,3301 +1,3300 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 <assert.h>
13 13 #include <ctype.h>
14 14 #include <limits.h>
15 15 #include <stddef.h>
16 16 #include <stdlib.h>
17 17 #include <string.h>
18 18 #include <structmember.h>
19 19
20 20 #include "bitmanipulation.h"
21 21 #include "charencode.h"
22 22 #include "compat.h"
23 23 #include "revlog.h"
24 24 #include "util.h"
25 25
26 26 typedef struct indexObjectStruct indexObject;
27 27
28 28 typedef struct {
29 29 int children[16];
30 30 } nodetreenode;
31 31
32 32 typedef struct {
33 33 int abi_version;
34 34 Py_ssize_t (*index_length)(const indexObject *);
35 35 const char *(*index_node)(indexObject *, Py_ssize_t);
36 36 int (*fast_rank)(indexObject *, Py_ssize_t);
37 37 int (*index_parents)(PyObject *, int, int *);
38 38 } Revlog_CAPI;
39 39
40 40 /*
41 41 * A base-16 trie for fast node->rev mapping.
42 42 *
43 43 * Positive value is index of the next node in the trie
44 44 * Negative value is a leaf: -(rev + 2)
45 45 * Zero is empty
46 46 */
47 47 typedef struct {
48 48 indexObject *index;
49 49 nodetreenode *nodes;
50 50 Py_ssize_t nodelen;
51 51 size_t length; /* # nodes in use */
52 52 size_t capacity; /* # nodes allocated */
53 53 int depth; /* maximum depth of tree */
54 54 int splits; /* # splits performed */
55 55 } nodetree;
56 56
57 57 typedef struct {
58 58 PyObject_HEAD /* ; */
59 59 nodetree nt;
60 60 } nodetreeObject;
61 61
62 62 /*
63 63 * This class has two behaviors.
64 64 *
65 65 * When used in a list-like way (with integer keys), we decode an
66 66 * entry in a RevlogNG index file on demand. We have limited support for
67 67 * integer-keyed insert and delete, only at elements right before the
68 68 * end.
69 69 *
70 70 * With string keys, we lazily perform a reverse mapping from node to
71 71 * rev, using a base-16 trie.
72 72 */
73 73 struct indexObjectStruct {
74 74 PyObject_HEAD
75 75 /* Type-specific fields go here. */
76 76 PyObject *data; /* raw bytes of index */
77 77 Py_ssize_t nodelen; /* digest size of the hash, 20 for SHA-1 */
78 78 PyObject *nullentry; /* fast path for references to null */
79 79 Py_buffer buf; /* buffer of data */
80 80 const char **offsets; /* populated on demand */
81 81 Py_ssize_t length; /* current on-disk number of elements */
82 82 unsigned new_length; /* number of added elements */
83 83 unsigned added_length; /* space reserved for added elements */
84 84 char *added; /* populated on demand */
85 85 PyObject *headrevs; /* cache, invalidated on changes */
86 86 PyObject *filteredrevs; /* filtered revs set */
87 87 nodetree nt; /* base-16 trie */
88 88 int ntinitialized; /* 0 or 1 */
89 89 int ntrev; /* last rev scanned */
90 90 int ntlookups; /* # lookups */
91 91 int ntmisses; /* # lookups that miss the cache */
92 92 int inlined;
93 93 long entry_size; /* size of index headers. Differs in v1 v.s. v2 format
94 94 */
95 95 long rust_ext_compat; /* compatibility with being used in rust
96 96 extensions */
97 97 long format_version; /* format version selector (format_*) */
98 98 };
99 99
100 100 static Py_ssize_t index_length(const indexObject *self)
101 101 {
102 102 return self->length + self->new_length;
103 103 }
104 104
105 105 static const char nullid[32] = {0};
106 106 static const Py_ssize_t nullrev = -1;
107 107
108 108 static Py_ssize_t inline_scan(indexObject *self, const char **offsets);
109 109
110 110 static int index_find_node(indexObject *self, const char *node);
111 111
112 112 #if LONG_MAX == 0x7fffffffL
113 113 static const char *const tuple_format = "Kiiiiiiy#KiBBi";
114 114 #else
115 115 static const char *const tuple_format = "kiiiiiiy#kiBBi";
116 116 #endif
117 117
118 118 /* A RevlogNG v1 index entry is 64 bytes long. */
119 119 static const long v1_entry_size = 64;
120 120
121 121 /* A Revlogv2 index entry is 96 bytes long. */
122 122 static const long v2_entry_size = 96;
123 123
124 124 /* A Changelogv2 index entry is 96 bytes long. */
125 125 static const long cl2_entry_size = 96;
126 126
127 127 /* Internal format version.
128 128 * Must match their counterparts in revlogutils/constants.py */
129 129 static const long format_v1 = 1; /* constants.py: REVLOGV1 */
130 130 static const long format_v2 = 0xDEAD; /* constants.py: REVLOGV2 */
131 131 static const long format_cl2 = 0xD34D; /* constants.py: CHANGELOGV2 */
132 132
133 133 static const long entry_v1_offset_high = 0;
134 134 static const long entry_v1_offset_offset_flags = 4;
135 135 static const long entry_v1_offset_comp_len = 8;
136 136 static const long entry_v1_offset_uncomp_len = 12;
137 137 static const long entry_v1_offset_base_rev = 16;
138 138 static const long entry_v1_offset_link_rev = 20;
139 139 static const long entry_v1_offset_parent_1 = 24;
140 140 static const long entry_v1_offset_parent_2 = 28;
141 141 static const long entry_v1_offset_node_id = 32;
142 142
143 143 static const long entry_v2_offset_high = 0;
144 144 static const long entry_v2_offset_offset_flags = 4;
145 145 static const long entry_v2_offset_comp_len = 8;
146 146 static const long entry_v2_offset_uncomp_len = 12;
147 147 static const long entry_v2_offset_base_rev = 16;
148 148 static const long entry_v2_offset_link_rev = 20;
149 149 static const long entry_v2_offset_parent_1 = 24;
150 150 static const long entry_v2_offset_parent_2 = 28;
151 151 static const long entry_v2_offset_node_id = 32;
152 152 static const long entry_v2_offset_sidedata_offset = 64;
153 153 static const long entry_v2_offset_sidedata_comp_len = 72;
154 154 static const long entry_v2_offset_all_comp_mode = 76;
155 155 /* next free offset: 77 */
156 156
157 157 static const long entry_cl2_offset_high = 0;
158 158 static const long entry_cl2_offset_offset_flags = 4;
159 159 static const long entry_cl2_offset_comp_len = 8;
160 160 static const long entry_cl2_offset_uncomp_len = 12;
161 161 static const long entry_cl2_offset_parent_1 = 16;
162 162 static const long entry_cl2_offset_parent_2 = 20;
163 163 static const long entry_cl2_offset_node_id = 24;
164 164 static const long entry_cl2_offset_sidedata_offset = 56;
165 165 static const long entry_cl2_offset_sidedata_comp_len = 64;
166 166 static const long entry_cl2_offset_all_comp_mode = 68;
167 167 static const long entry_cl2_offset_rank = 69;
168 168 /* next free offset: 73 */
169 169
170 170 static const char comp_mode_inline = 2;
171 171 static const char rank_unknown = -1;
172 172
173 173 static void raise_revlog_error(void)
174 174 {
175 175 PyObject *mod = NULL, *dict = NULL, *errclass = NULL;
176 176
177 177 mod = PyImport_ImportModule("mercurial.error");
178 178 if (mod == NULL) {
179 179 goto cleanup;
180 180 }
181 181
182 182 dict = PyModule_GetDict(mod);
183 183 if (dict == NULL) {
184 184 goto cleanup;
185 185 }
186 186 Py_INCREF(dict);
187 187
188 188 errclass = PyDict_GetItemString(dict, "RevlogError");
189 189 if (errclass == NULL) {
190 190 PyErr_SetString(PyExc_SystemError,
191 191 "could not find RevlogError");
192 192 goto cleanup;
193 193 }
194 194
195 195 /* value of exception is ignored by callers */
196 196 PyErr_SetString(errclass, "RevlogError");
197 197
198 198 cleanup:
199 199 Py_XDECREF(dict);
200 200 Py_XDECREF(mod);
201 201 }
202 202
203 203 /*
204 204 * Return a pointer to the beginning of a RevlogNG record.
205 205 */
206 206 static const char *index_deref(indexObject *self, Py_ssize_t pos)
207 207 {
208 208 if (pos >= self->length)
209 209 return self->added + (pos - self->length) * self->entry_size;
210 210
211 211 if (self->inlined && pos > 0) {
212 212 if (self->offsets == NULL) {
213 213 Py_ssize_t ret;
214 214 self->offsets =
215 215 PyMem_Malloc(self->length * sizeof(*self->offsets));
216 216 if (self->offsets == NULL)
217 217 return (const char *)PyErr_NoMemory();
218 218 ret = inline_scan(self, self->offsets);
219 219 if (ret == -1) {
220 220 return NULL;
221 221 };
222 222 }
223 223 return self->offsets[pos];
224 224 }
225 225
226 226 return (const char *)(self->buf.buf) + pos * self->entry_size;
227 227 }
228 228
229 229 /*
230 230 * Get parents of the given rev.
231 231 *
232 232 * The specified rev must be valid and must not be nullrev. A returned
233 233 * parent revision may be nullrev, but is guaranteed to be in valid range.
234 234 */
235 235 static inline int index_get_parents(indexObject *self, Py_ssize_t rev, int *ps,
236 236 int maxrev)
237 237 {
238 238 const char *data = index_deref(self, rev);
239 239
240 240 if (self->format_version == format_v1) {
241 241 ps[0] = getbe32(data + entry_v1_offset_parent_1);
242 242 ps[1] = getbe32(data + entry_v1_offset_parent_2);
243 243 } else if (self->format_version == format_v2) {
244 244 ps[0] = getbe32(data + entry_v2_offset_parent_1);
245 245 ps[1] = getbe32(data + entry_v2_offset_parent_2);
246 246 } else if (self->format_version == format_cl2) {
247 247 ps[0] = getbe32(data + entry_cl2_offset_parent_1);
248 248 ps[1] = getbe32(data + entry_cl2_offset_parent_2);
249 249 } else {
250 250 raise_revlog_error();
251 251 return -1;
252 252 }
253 253
254 254 /* If index file is corrupted, ps[] may point to invalid revisions. So
255 255 * there is a risk of buffer overflow to trust them unconditionally. */
256 256 if (ps[0] < -1 || ps[0] > maxrev || ps[1] < -1 || ps[1] > maxrev) {
257 257 PyErr_SetString(PyExc_ValueError, "parent out of range");
258 258 return -1;
259 259 }
260 260 return 0;
261 261 }
262 262
263 263 /*
264 264 * Get parents of the given rev.
265 265 *
266 266 * If the specified rev is out of range, IndexError will be raised. If the
267 267 * revlog entry is corrupted, ValueError may be raised.
268 268 *
269 269 * Returns 0 on success or -1 on failure.
270 270 */
271 271 static int HgRevlogIndex_GetParents(PyObject *op, int rev, int *ps)
272 272 {
273 273 int tiprev;
274 274 if (!op || !HgRevlogIndex_Check(op) || !ps) {
275 275 PyErr_BadInternalCall();
276 276 return -1;
277 277 }
278 278 tiprev = (int)index_length((indexObject *)op) - 1;
279 279 if (rev < -1 || rev > tiprev) {
280 280 PyErr_Format(PyExc_IndexError, "rev out of range: %d", rev);
281 281 return -1;
282 282 } else if (rev == -1) {
283 283 ps[0] = ps[1] = -1;
284 284 return 0;
285 285 } else {
286 286 return index_get_parents((indexObject *)op, rev, ps, tiprev);
287 287 }
288 288 }
289 289
290 290 static inline int64_t index_get_start(indexObject *self, Py_ssize_t rev)
291 291 {
292 292 const char *data;
293 293 uint64_t offset;
294 294
295 295 if (rev == nullrev)
296 296 return 0;
297 297
298 298 data = index_deref(self, rev);
299 299
300 300 if (self->format_version == format_v1) {
301 301 offset = getbe32(data + entry_v1_offset_offset_flags);
302 302 if (rev == 0) {
303 303 /* mask out version number for the first entry */
304 304 offset &= 0xFFFF;
305 305 } else {
306 306 uint32_t offset_high =
307 307 getbe32(data + entry_v1_offset_high);
308 308 offset |= ((uint64_t)offset_high) << 32;
309 309 }
310 310 } else if (self->format_version == format_v2) {
311 311 offset = getbe32(data + entry_v2_offset_offset_flags);
312 312 if (rev == 0) {
313 313 /* mask out version number for the first entry */
314 314 offset &= 0xFFFF;
315 315 } else {
316 316 uint32_t offset_high =
317 317 getbe32(data + entry_v2_offset_high);
318 318 offset |= ((uint64_t)offset_high) << 32;
319 319 }
320 320 } else if (self->format_version == format_cl2) {
321 321 uint32_t offset_high = getbe32(data + entry_cl2_offset_high);
322 322 offset = getbe32(data + entry_cl2_offset_offset_flags);
323 323 offset |= ((uint64_t)offset_high) << 32;
324 324 } else {
325 325 raise_revlog_error();
326 326 return -1;
327 327 }
328 328
329 329 return (int64_t)(offset >> 16);
330 330 }
331 331
332 332 static inline int index_get_length(indexObject *self, Py_ssize_t rev)
333 333 {
334 334 const char *data;
335 335 int tmp;
336 336
337 337 if (rev == nullrev)
338 338 return 0;
339 339
340 340 data = index_deref(self, rev);
341 341
342 342 if (self->format_version == format_v1) {
343 343 tmp = (int)getbe32(data + entry_v1_offset_comp_len);
344 344 } else if (self->format_version == format_v2) {
345 345 tmp = (int)getbe32(data + entry_v2_offset_comp_len);
346 346 } else if (self->format_version == format_cl2) {
347 347 tmp = (int)getbe32(data + entry_cl2_offset_comp_len);
348 348 } else {
349 349 raise_revlog_error();
350 350 return -1;
351 351 }
352 352 if (tmp < 0) {
353 353 PyErr_Format(PyExc_OverflowError,
354 354 "revlog entry size out of bound (%d)", tmp);
355 355 return -1;
356 356 }
357 357 return tmp;
358 358 }
359 359
360 360 /*
361 361 * RevlogNG format (all in big endian, data may be inlined):
362 362 * 6 bytes: offset
363 363 * 2 bytes: flags
364 364 * 4 bytes: compressed length
365 365 * 4 bytes: uncompressed length
366 366 * 4 bytes: base revision
367 367 * 4 bytes: link revision
368 368 * 4 bytes: parent 1 revision
369 369 * 4 bytes: parent 2 revision
370 370 * 32 bytes: nodeid (only 20 bytes used with SHA-1)
371 371 */
372 372 static PyObject *index_get(indexObject *self, Py_ssize_t pos)
373 373 {
374 374 uint64_t offset_flags, sidedata_offset;
375 375 int comp_len, uncomp_len, base_rev, link_rev, parent_1, parent_2,
376 376 sidedata_comp_len, rank = rank_unknown;
377 377 char data_comp_mode, sidedata_comp_mode;
378 378 const char *c_node_id;
379 379 const char *data;
380 380 Py_ssize_t length = index_length(self);
381 381
382 382 if (pos == nullrev) {
383 383 Py_INCREF(self->nullentry);
384 384 return self->nullentry;
385 385 }
386 386
387 387 if (pos < 0 || pos >= length) {
388 388 PyErr_SetString(PyExc_IndexError, "revlog index out of range");
389 389 return NULL;
390 390 }
391 391
392 392 data = index_deref(self, pos);
393 393 if (data == NULL)
394 394 return NULL;
395 395
396 396 if (self->format_version == format_v1) {
397 397 offset_flags = getbe32(data + entry_v1_offset_offset_flags);
398 398 /*
399 399 * The first entry on-disk needs the version number masked out,
400 400 * but this doesn't apply if entries are added to an empty
401 401 * index.
402 402 */
403 403 if (self->length && pos == 0)
404 404 offset_flags &= 0xFFFF;
405 405 else {
406 406 uint32_t offset_high =
407 407 getbe32(data + entry_v1_offset_high);
408 408 offset_flags |= ((uint64_t)offset_high) << 32;
409 409 }
410 410
411 411 comp_len = getbe32(data + entry_v1_offset_comp_len);
412 412 uncomp_len = getbe32(data + entry_v1_offset_uncomp_len);
413 413 base_rev = getbe32(data + entry_v1_offset_base_rev);
414 414 link_rev = getbe32(data + entry_v1_offset_link_rev);
415 415 parent_1 = getbe32(data + entry_v1_offset_parent_1);
416 416 parent_2 = getbe32(data + entry_v1_offset_parent_2);
417 417 c_node_id = data + entry_v1_offset_node_id;
418 418
419 419 sidedata_offset = 0;
420 420 sidedata_comp_len = 0;
421 421 data_comp_mode = comp_mode_inline;
422 422 sidedata_comp_mode = comp_mode_inline;
423 423 } else if (self->format_version == format_v2) {
424 424 offset_flags = getbe32(data + entry_v2_offset_offset_flags);
425 425 /*
426 426 * The first entry on-disk needs the version number masked out,
427 427 * but this doesn't apply if entries are added to an empty
428 428 * index.
429 429 */
430 430 if (self->length && pos == 0)
431 431 offset_flags &= 0xFFFF;
432 432 else {
433 433 uint32_t offset_high =
434 434 getbe32(data + entry_v2_offset_high);
435 435 offset_flags |= ((uint64_t)offset_high) << 32;
436 436 }
437 437
438 438 comp_len = getbe32(data + entry_v2_offset_comp_len);
439 439 uncomp_len = getbe32(data + entry_v2_offset_uncomp_len);
440 440 base_rev = getbe32(data + entry_v2_offset_base_rev);
441 441 link_rev = getbe32(data + entry_v2_offset_link_rev);
442 442 parent_1 = getbe32(data + entry_v2_offset_parent_1);
443 443 parent_2 = getbe32(data + entry_v2_offset_parent_2);
444 444 c_node_id = data + entry_v2_offset_node_id;
445 445
446 446 sidedata_offset =
447 447 getbe64(data + entry_v2_offset_sidedata_offset);
448 448 sidedata_comp_len =
449 449 getbe32(data + entry_v2_offset_sidedata_comp_len);
450 450 data_comp_mode = data[entry_v2_offset_all_comp_mode] & 3;
451 451 sidedata_comp_mode =
452 452 ((data[entry_v2_offset_all_comp_mode] >> 2) & 3);
453 453 } else if (self->format_version == format_cl2) {
454 454 uint32_t offset_high = getbe32(data + entry_cl2_offset_high);
455 455 offset_flags = getbe32(data + entry_cl2_offset_offset_flags);
456 456 offset_flags |= ((uint64_t)offset_high) << 32;
457 457 comp_len = getbe32(data + entry_cl2_offset_comp_len);
458 458 uncomp_len = getbe32(data + entry_cl2_offset_uncomp_len);
459 459 /* base_rev and link_rev are not stored in changelogv2, but are
460 460 still used by some functions shared with the other revlogs.
461 461 They are supposed to contain links to other revisions,
462 462 but they always point to themselves in the case of a changelog.
463 463 */
464 464 base_rev = pos;
465 465 link_rev = pos;
466 466 parent_1 = getbe32(data + entry_cl2_offset_parent_1);
467 467 parent_2 = getbe32(data + entry_cl2_offset_parent_2);
468 468 c_node_id = data + entry_cl2_offset_node_id;
469 469 sidedata_offset =
470 470 getbe64(data + entry_cl2_offset_sidedata_offset);
471 471 sidedata_comp_len =
472 472 getbe32(data + entry_cl2_offset_sidedata_comp_len);
473 473 data_comp_mode = data[entry_cl2_offset_all_comp_mode] & 3;
474 474 sidedata_comp_mode =
475 475 ((data[entry_cl2_offset_all_comp_mode] >> 2) & 3);
476 476 rank = getbe32(data + entry_cl2_offset_rank);
477 477 } else {
478 478 raise_revlog_error();
479 479 return NULL;
480 480 }
481 481
482 482 return Py_BuildValue(tuple_format, offset_flags, comp_len, uncomp_len,
483 483 base_rev, link_rev, parent_1, parent_2, c_node_id,
484 484 self->nodelen, sidedata_offset, sidedata_comp_len,
485 485 data_comp_mode, sidedata_comp_mode, rank);
486 486 }
487 487 /*
488 488 * Pack header information in binary
489 489 */
490 490 static PyObject *index_pack_header(indexObject *self, PyObject *args)
491 491 {
492 492 int header;
493 493 char out[4];
494 494 if (!PyArg_ParseTuple(args, "I", &header)) {
495 495 return NULL;
496 496 }
497 497 if (self->format_version != format_v1) {
498 498 PyErr_Format(PyExc_RuntimeError,
499 499 "version header should go in the docket, not the "
500 500 "index: %lu",
501 501 header);
502 502 return NULL;
503 503 }
504 504 putbe32(header, out);
505 505 return PyBytes_FromStringAndSize(out, 4);
506 506 }
507 507 /*
508 508 * Return the raw binary string representing a revision
509 509 */
510 510 static PyObject *index_entry_binary(indexObject *self, PyObject *value)
511 511 {
512 512 long rev;
513 513 const char *data;
514 514 Py_ssize_t length = index_length(self);
515 515
516 516 if (!pylong_to_long(value, &rev)) {
517 517 return NULL;
518 518 }
519 519 if (rev < 0 || rev >= length) {
520 520 PyErr_Format(PyExc_ValueError, "revlog index out of range: %ld",
521 521 rev);
522 522 return NULL;
523 523 };
524 524
525 525 data = index_deref(self, rev);
526 526 if (data == NULL)
527 527 return NULL;
528 528 if (rev == 0 && self->format_version == format_v1) {
529 529 /* the header is eating the start of the first entry */
530 530 return PyBytes_FromStringAndSize(data + 4,
531 531 self->entry_size - 4);
532 532 }
533 533 return PyBytes_FromStringAndSize(data, self->entry_size);
534 534 }
535 535
536 536 /*
537 537 * Return the hash of node corresponding to the given rev.
538 538 */
539 539 static const char *index_node(indexObject *self, Py_ssize_t pos)
540 540 {
541 541 Py_ssize_t length = index_length(self);
542 542 const char *data;
543 543 const char *node_id;
544 544
545 545 if (pos == nullrev)
546 546 return nullid;
547 547
548 548 if (pos >= length)
549 549 return NULL;
550 550
551 551 data = index_deref(self, pos);
552 552
553 553 if (self->format_version == format_v1) {
554 554 node_id = data + entry_v1_offset_node_id;
555 555 } else if (self->format_version == format_v2) {
556 556 node_id = data + entry_v2_offset_node_id;
557 557 } else if (self->format_version == format_cl2) {
558 558 node_id = data + entry_cl2_offset_node_id;
559 559 } else {
560 560 raise_revlog_error();
561 561 return NULL;
562 562 }
563 563
564 564 return data ? node_id : NULL;
565 565 }
566 566
567 567 /*
568 568 * Return the stored rank of a given revision if known, or rank_unknown
569 569 * otherwise.
570 570 *
571 571 * The rank of a revision is the size of the sub-graph it defines as a head.
572 572 * Equivalently, the rank of a revision `r` is the size of the set
573 573 * `ancestors(r)`, `r` included.
574 574 *
575 575 * This method returns the rank retrieved from the revlog in constant time. It
576 576 * makes no attempt at computing unknown values for versions of the revlog
577 577 * which do not persist the rank.
578 578 */
579 579 static int index_fast_rank(indexObject *self, Py_ssize_t pos)
580 580 {
581 581 Py_ssize_t length = index_length(self);
582 int rank;
583 582
584 583 if (self->format_version != format_cl2 || pos >= length) {
585 584 return rank_unknown;
586 585 }
587 586
588 587 if (pos == nullrev) {
589 588 return 0; /* convention */
590 589 }
591 590
592 591 return *(index_deref(self, pos) + entry_cl2_offset_rank);
593 592 }
594 593
595 594 /*
596 595 * Return the hash of the node corresponding to the given rev. The
597 596 * rev is assumed to be existing. If not, an exception is set.
598 597 */
599 598 static const char *index_node_existing(indexObject *self, Py_ssize_t pos)
600 599 {
601 600 const char *node = index_node(self, pos);
602 601 if (node == NULL) {
603 602 PyErr_Format(PyExc_IndexError, "could not access rev %d",
604 603 (int)pos);
605 604 }
606 605 return node;
607 606 }
608 607
609 608 static int nt_insert(nodetree *self, const char *node, int rev);
610 609
611 610 static int node_check(Py_ssize_t nodelen, PyObject *obj, char **node)
612 611 {
613 612 Py_ssize_t thisnodelen;
614 613 if (PyBytes_AsStringAndSize(obj, node, &thisnodelen) == -1)
615 614 return -1;
616 615 if (nodelen == thisnodelen)
617 616 return 0;
618 617 PyErr_Format(PyExc_ValueError, "node len %zd != expected node len %zd",
619 618 thisnodelen, nodelen);
620 619 return -1;
621 620 }
622 621
623 622 static PyObject *index_append(indexObject *self, PyObject *obj)
624 623 {
625 624 uint64_t offset_flags, sidedata_offset;
626 625 int rev, comp_len, uncomp_len, base_rev, link_rev, parent_1, parent_2,
627 626 sidedata_comp_len, rank;
628 627 char data_comp_mode, sidedata_comp_mode;
629 628 Py_ssize_t c_node_id_len;
630 629 const char *c_node_id;
631 630 char comp_field;
632 631 char *data;
633 632
634 633 if (!PyArg_ParseTuple(obj, tuple_format, &offset_flags, &comp_len,
635 634 &uncomp_len, &base_rev, &link_rev, &parent_1,
636 635 &parent_2, &c_node_id, &c_node_id_len,
637 636 &sidedata_offset, &sidedata_comp_len,
638 637 &data_comp_mode, &sidedata_comp_mode, &rank)) {
639 638 PyErr_SetString(PyExc_TypeError, "12-tuple required");
640 639 return NULL;
641 640 }
642 641
643 642 if (c_node_id_len != self->nodelen) {
644 643 PyErr_SetString(PyExc_TypeError, "invalid node");
645 644 return NULL;
646 645 }
647 646 if (self->format_version == format_v1) {
648 647
649 648 if (data_comp_mode != comp_mode_inline) {
650 649 PyErr_Format(PyExc_ValueError,
651 650 "invalid data compression mode: %i",
652 651 data_comp_mode);
653 652 return NULL;
654 653 }
655 654 if (sidedata_comp_mode != comp_mode_inline) {
656 655 PyErr_Format(PyExc_ValueError,
657 656 "invalid sidedata compression mode: %i",
658 657 sidedata_comp_mode);
659 658 return NULL;
660 659 }
661 660 }
662 661
663 662 if (self->new_length == self->added_length) {
664 663 size_t new_added_length =
665 664 self->added_length ? self->added_length * 2 : 4096;
666 665 void *new_added = PyMem_Realloc(
667 666 self->added, new_added_length * self->entry_size);
668 667 if (!new_added)
669 668 return PyErr_NoMemory();
670 669 self->added = new_added;
671 670 self->added_length = new_added_length;
672 671 }
673 672 rev = self->length + self->new_length;
674 673 data = self->added + self->entry_size * self->new_length++;
675 674
676 675 memset(data, 0, self->entry_size);
677 676
678 677 if (self->format_version == format_v1) {
679 678 putbe32(offset_flags >> 32, data + entry_v1_offset_high);
680 679 putbe32(offset_flags & 0xffffffffU,
681 680 data + entry_v1_offset_offset_flags);
682 681 putbe32(comp_len, data + entry_v1_offset_comp_len);
683 682 putbe32(uncomp_len, data + entry_v1_offset_uncomp_len);
684 683 putbe32(base_rev, data + entry_v1_offset_base_rev);
685 684 putbe32(link_rev, data + entry_v1_offset_link_rev);
686 685 putbe32(parent_1, data + entry_v1_offset_parent_1);
687 686 putbe32(parent_2, data + entry_v1_offset_parent_2);
688 687 memcpy(data + entry_v1_offset_node_id, c_node_id,
689 688 c_node_id_len);
690 689 } else if (self->format_version == format_v2) {
691 690 putbe32(offset_flags >> 32, data + entry_v2_offset_high);
692 691 putbe32(offset_flags & 0xffffffffU,
693 692 data + entry_v2_offset_offset_flags);
694 693 putbe32(comp_len, data + entry_v2_offset_comp_len);
695 694 putbe32(uncomp_len, data + entry_v2_offset_uncomp_len);
696 695 putbe32(base_rev, data + entry_v2_offset_base_rev);
697 696 putbe32(link_rev, data + entry_v2_offset_link_rev);
698 697 putbe32(parent_1, data + entry_v2_offset_parent_1);
699 698 putbe32(parent_2, data + entry_v2_offset_parent_2);
700 699 memcpy(data + entry_v2_offset_node_id, c_node_id,
701 700 c_node_id_len);
702 701 putbe64(sidedata_offset,
703 702 data + entry_v2_offset_sidedata_offset);
704 703 putbe32(sidedata_comp_len,
705 704 data + entry_v2_offset_sidedata_comp_len);
706 705 comp_field = data_comp_mode & 3;
707 706 comp_field = comp_field | (sidedata_comp_mode & 3) << 2;
708 707 data[entry_v2_offset_all_comp_mode] = comp_field;
709 708 } else if (self->format_version == format_cl2) {
710 709 putbe32(offset_flags >> 32, data + entry_cl2_offset_high);
711 710 putbe32(offset_flags & 0xffffffffU,
712 711 data + entry_cl2_offset_offset_flags);
713 712 putbe32(comp_len, data + entry_cl2_offset_comp_len);
714 713 putbe32(uncomp_len, data + entry_cl2_offset_uncomp_len);
715 714 putbe32(parent_1, data + entry_cl2_offset_parent_1);
716 715 putbe32(parent_2, data + entry_cl2_offset_parent_2);
717 716 memcpy(data + entry_cl2_offset_node_id, c_node_id,
718 717 c_node_id_len);
719 718 putbe64(sidedata_offset,
720 719 data + entry_cl2_offset_sidedata_offset);
721 720 putbe32(sidedata_comp_len,
722 721 data + entry_cl2_offset_sidedata_comp_len);
723 722 comp_field = data_comp_mode & 3;
724 723 comp_field = comp_field | (sidedata_comp_mode & 3) << 2;
725 724 data[entry_cl2_offset_all_comp_mode] = comp_field;
726 725 putbe32(rank, data + entry_cl2_offset_rank);
727 726 } else {
728 727 raise_revlog_error();
729 728 return NULL;
730 729 }
731 730
732 731 if (self->ntinitialized)
733 732 nt_insert(&self->nt, c_node_id, rev);
734 733
735 734 Py_CLEAR(self->headrevs);
736 735 Py_RETURN_NONE;
737 736 }
738 737
739 738 /* Replace an existing index entry's sidedata offset and length with new ones.
740 739 This cannot be used outside of the context of sidedata rewriting,
741 740 inside the transaction that creates the given revision. */
742 741 static PyObject *index_replace_sidedata_info(indexObject *self, PyObject *args)
743 742 {
744 743 uint64_t offset_flags, sidedata_offset;
745 744 Py_ssize_t rev;
746 745 int sidedata_comp_len;
747 746 char comp_mode;
748 747 char *data;
749 748 #if LONG_MAX == 0x7fffffffL
750 749 const char *const sidedata_format = "nKiKB";
751 750 #else
752 751 const char *const sidedata_format = "nkikB";
753 752 #endif
754 753
755 754 if (self->entry_size == v1_entry_size || self->inlined) {
756 755 /*
757 756 There is a bug in the transaction handling when going from an
758 757 inline revlog to a separate index and data file. Turn it off until
759 758 it's fixed, since v2 revlogs sometimes get rewritten on exchange.
760 759 See issue6485.
761 760 */
762 761 raise_revlog_error();
763 762 return NULL;
764 763 }
765 764
766 765 if (!PyArg_ParseTuple(args, sidedata_format, &rev, &sidedata_offset,
767 766 &sidedata_comp_len, &offset_flags, &comp_mode))
768 767 return NULL;
769 768
770 769 if (rev < 0 || rev >= index_length(self)) {
771 770 PyErr_SetString(PyExc_IndexError, "revision outside index");
772 771 return NULL;
773 772 }
774 773 if (rev < self->length) {
775 774 PyErr_SetString(
776 775 PyExc_IndexError,
777 776 "cannot rewrite entries outside of this transaction");
778 777 return NULL;
779 778 }
780 779
781 780 /* Find the newly added node, offset from the "already on-disk" length
782 781 */
783 782 data = self->added + self->entry_size * (rev - self->length);
784 783 if (self->format_version == format_v2) {
785 784 putbe64(offset_flags, data + entry_v2_offset_high);
786 785 putbe64(sidedata_offset,
787 786 data + entry_v2_offset_sidedata_offset);
788 787 putbe32(sidedata_comp_len,
789 788 data + entry_v2_offset_sidedata_comp_len);
790 789 data[entry_v2_offset_all_comp_mode] =
791 790 (data[entry_v2_offset_all_comp_mode] & ~(3 << 2)) |
792 791 ((comp_mode & 3) << 2);
793 792 } else if (self->format_version == format_cl2) {
794 793 putbe64(offset_flags, data + entry_cl2_offset_high);
795 794 putbe64(sidedata_offset,
796 795 data + entry_cl2_offset_sidedata_offset);
797 796 putbe32(sidedata_comp_len,
798 797 data + entry_cl2_offset_sidedata_comp_len);
799 798 data[entry_cl2_offset_all_comp_mode] =
800 799 (data[entry_cl2_offset_all_comp_mode] & ~(3 << 2)) |
801 800 ((comp_mode & 3) << 2);
802 801 } else {
803 802 raise_revlog_error();
804 803 return NULL;
805 804 }
806 805
807 806 Py_RETURN_NONE;
808 807 }
809 808
810 809 static PyObject *index_stats(indexObject *self)
811 810 {
812 811 PyObject *obj = PyDict_New();
813 812 PyObject *s = NULL;
814 813 PyObject *t = NULL;
815 814
816 815 if (obj == NULL)
817 816 return NULL;
818 817
819 818 #define istat(__n, __d) \
820 819 do { \
821 820 s = PyBytes_FromString(__d); \
822 821 t = PyLong_FromSsize_t(self->__n); \
823 822 if (!s || !t) \
824 823 goto bail; \
825 824 if (PyDict_SetItem(obj, s, t) == -1) \
826 825 goto bail; \
827 826 Py_CLEAR(s); \
828 827 Py_CLEAR(t); \
829 828 } while (0)
830 829
831 830 if (self->added_length)
832 831 istat(new_length, "index entries added");
833 832 istat(length, "revs in memory");
834 833 istat(ntlookups, "node trie lookups");
835 834 istat(ntmisses, "node trie misses");
836 835 istat(ntrev, "node trie last rev scanned");
837 836 if (self->ntinitialized) {
838 837 istat(nt.capacity, "node trie capacity");
839 838 istat(nt.depth, "node trie depth");
840 839 istat(nt.length, "node trie count");
841 840 istat(nt.splits, "node trie splits");
842 841 }
843 842
844 843 #undef istat
845 844
846 845 return obj;
847 846
848 847 bail:
849 848 Py_XDECREF(obj);
850 849 Py_XDECREF(s);
851 850 Py_XDECREF(t);
852 851 return NULL;
853 852 }
854 853
855 854 /*
856 855 * When we cache a list, we want to be sure the caller can't mutate
857 856 * the cached copy.
858 857 */
859 858 static PyObject *list_copy(PyObject *list)
860 859 {
861 860 Py_ssize_t len = PyList_GET_SIZE(list);
862 861 PyObject *newlist = PyList_New(len);
863 862 Py_ssize_t i;
864 863
865 864 if (newlist == NULL)
866 865 return NULL;
867 866
868 867 for (i = 0; i < len; i++) {
869 868 PyObject *obj = PyList_GET_ITEM(list, i);
870 869 Py_INCREF(obj);
871 870 PyList_SET_ITEM(newlist, i, obj);
872 871 }
873 872
874 873 return newlist;
875 874 }
876 875
877 876 static int check_filter(PyObject *filter, Py_ssize_t arg)
878 877 {
879 878 if (filter) {
880 879 PyObject *arglist, *result;
881 880 int isfiltered;
882 881
883 882 arglist = Py_BuildValue("(n)", arg);
884 883 if (!arglist) {
885 884 return -1;
886 885 }
887 886
888 887 result = PyObject_Call(filter, arglist, NULL);
889 888 Py_DECREF(arglist);
890 889 if (!result) {
891 890 return -1;
892 891 }
893 892
894 893 /* PyObject_IsTrue returns 1 if true, 0 if false, -1 if error,
895 894 * same as this function, so we can just return it directly.*/
896 895 isfiltered = PyObject_IsTrue(result);
897 896 Py_DECREF(result);
898 897 return isfiltered;
899 898 } else {
900 899 return 0;
901 900 }
902 901 }
903 902
904 903 static inline void set_phase_from_parents(char *phases, int parent_1,
905 904 int parent_2, Py_ssize_t i)
906 905 {
907 906 if (parent_1 >= 0 && phases[parent_1] > phases[i])
908 907 phases[i] = phases[parent_1];
909 908 if (parent_2 >= 0 && phases[parent_2] > phases[i])
910 909 phases[i] = phases[parent_2];
911 910 }
912 911
913 912 static PyObject *reachableroots2(indexObject *self, PyObject *args)
914 913 {
915 914
916 915 /* Input */
917 916 long minroot;
918 917 PyObject *includepatharg = NULL;
919 918 int includepath = 0;
920 919 /* heads and roots are lists */
921 920 PyObject *heads = NULL;
922 921 PyObject *roots = NULL;
923 922 PyObject *reachable = NULL;
924 923
925 924 PyObject *val;
926 925 Py_ssize_t len = index_length(self);
927 926 long revnum;
928 927 Py_ssize_t k;
929 928 Py_ssize_t i;
930 929 Py_ssize_t l;
931 930 int r;
932 931 int parents[2];
933 932
934 933 /* Internal data structure:
935 934 * tovisit: array of length len+1 (all revs + nullrev), filled upto
936 935 * lentovisit
937 936 *
938 937 * revstates: array of length len+1 (all revs + nullrev) */
939 938 int *tovisit = NULL;
940 939 long lentovisit = 0;
941 940 enum { RS_SEEN = 1, RS_ROOT = 2, RS_REACHABLE = 4 };
942 941 char *revstates = NULL;
943 942
944 943 /* Get arguments */
945 944 if (!PyArg_ParseTuple(args, "lO!O!O!", &minroot, &PyList_Type, &heads,
946 945 &PyList_Type, &roots, &PyBool_Type,
947 946 &includepatharg))
948 947 goto bail;
949 948
950 949 if (includepatharg == Py_True)
951 950 includepath = 1;
952 951
953 952 /* Initialize return set */
954 953 reachable = PyList_New(0);
955 954 if (reachable == NULL)
956 955 goto bail;
957 956
958 957 /* Initialize internal datastructures */
959 958 tovisit = (int *)malloc((len + 1) * sizeof(int));
960 959 if (tovisit == NULL) {
961 960 PyErr_NoMemory();
962 961 goto bail;
963 962 }
964 963
965 964 revstates = (char *)calloc(len + 1, 1);
966 965 if (revstates == NULL) {
967 966 PyErr_NoMemory();
968 967 goto bail;
969 968 }
970 969
971 970 l = PyList_GET_SIZE(roots);
972 971 for (i = 0; i < l; i++) {
973 972 revnum = PyLong_AsLong(PyList_GET_ITEM(roots, i));
974 973 if (revnum == -1 && PyErr_Occurred())
975 974 goto bail;
976 975 /* If root is out of range, e.g. wdir(), it must be unreachable
977 976 * from heads. So we can just ignore it. */
978 977 if (revnum + 1 < 0 || revnum + 1 >= len + 1)
979 978 continue;
980 979 revstates[revnum + 1] |= RS_ROOT;
981 980 }
982 981
983 982 /* Populate tovisit with all the heads */
984 983 l = PyList_GET_SIZE(heads);
985 984 for (i = 0; i < l; i++) {
986 985 revnum = PyLong_AsLong(PyList_GET_ITEM(heads, i));
987 986 if (revnum == -1 && PyErr_Occurred())
988 987 goto bail;
989 988 if (revnum + 1 < 0 || revnum + 1 >= len + 1) {
990 989 PyErr_SetString(PyExc_IndexError, "head out of range");
991 990 goto bail;
992 991 }
993 992 if (!(revstates[revnum + 1] & RS_SEEN)) {
994 993 tovisit[lentovisit++] = (int)revnum;
995 994 revstates[revnum + 1] |= RS_SEEN;
996 995 }
997 996 }
998 997
999 998 /* Visit the tovisit list and find the reachable roots */
1000 999 k = 0;
1001 1000 while (k < lentovisit) {
1002 1001 /* Add the node to reachable if it is a root*/
1003 1002 revnum = tovisit[k++];
1004 1003 if (revstates[revnum + 1] & RS_ROOT) {
1005 1004 revstates[revnum + 1] |= RS_REACHABLE;
1006 1005 val = PyLong_FromLong(revnum);
1007 1006 if (val == NULL)
1008 1007 goto bail;
1009 1008 r = PyList_Append(reachable, val);
1010 1009 Py_DECREF(val);
1011 1010 if (r < 0)
1012 1011 goto bail;
1013 1012 if (includepath == 0)
1014 1013 continue;
1015 1014 }
1016 1015
1017 1016 /* Add its parents to the list of nodes to visit */
1018 1017 if (revnum == nullrev)
1019 1018 continue;
1020 1019 r = index_get_parents(self, revnum, parents, (int)len - 1);
1021 1020 if (r < 0)
1022 1021 goto bail;
1023 1022 for (i = 0; i < 2; i++) {
1024 1023 if (!(revstates[parents[i] + 1] & RS_SEEN) &&
1025 1024 parents[i] >= minroot) {
1026 1025 tovisit[lentovisit++] = parents[i];
1027 1026 revstates[parents[i] + 1] |= RS_SEEN;
1028 1027 }
1029 1028 }
1030 1029 }
1031 1030
1032 1031 /* Find all the nodes in between the roots we found and the heads
1033 1032 * and add them to the reachable set */
1034 1033 if (includepath == 1) {
1035 1034 long minidx = minroot;
1036 1035 if (minidx < 0)
1037 1036 minidx = 0;
1038 1037 for (i = minidx; i < len; i++) {
1039 1038 if (!(revstates[i + 1] & RS_SEEN))
1040 1039 continue;
1041 1040 r = index_get_parents(self, i, parents, (int)len - 1);
1042 1041 /* Corrupted index file, error is set from
1043 1042 * index_get_parents */
1044 1043 if (r < 0)
1045 1044 goto bail;
1046 1045 if (((revstates[parents[0] + 1] |
1047 1046 revstates[parents[1] + 1]) &
1048 1047 RS_REACHABLE) &&
1049 1048 !(revstates[i + 1] & RS_REACHABLE)) {
1050 1049 revstates[i + 1] |= RS_REACHABLE;
1051 1050 val = PyLong_FromSsize_t(i);
1052 1051 if (val == NULL)
1053 1052 goto bail;
1054 1053 r = PyList_Append(reachable, val);
1055 1054 Py_DECREF(val);
1056 1055 if (r < 0)
1057 1056 goto bail;
1058 1057 }
1059 1058 }
1060 1059 }
1061 1060
1062 1061 free(revstates);
1063 1062 free(tovisit);
1064 1063 return reachable;
1065 1064 bail:
1066 1065 Py_XDECREF(reachable);
1067 1066 free(revstates);
1068 1067 free(tovisit);
1069 1068 return NULL;
1070 1069 }
1071 1070
1072 1071 static int add_roots_get_min(indexObject *self, PyObject *roots, char *phases,
1073 1072 char phase)
1074 1073 {
1075 1074 Py_ssize_t len = index_length(self);
1076 1075 PyObject *item;
1077 1076 PyObject *iterator;
1078 1077 int rev, minrev = -1;
1079 1078 char *node;
1080 1079
1081 1080 if (!PySet_Check(roots)) {
1082 1081 PyErr_SetString(PyExc_TypeError,
1083 1082 "roots must be a set of nodes");
1084 1083 return -2;
1085 1084 }
1086 1085 iterator = PyObject_GetIter(roots);
1087 1086 if (iterator == NULL)
1088 1087 return -2;
1089 1088 while ((item = PyIter_Next(iterator))) {
1090 1089 if (node_check(self->nodelen, item, &node) == -1)
1091 1090 goto failed;
1092 1091 rev = index_find_node(self, node);
1093 1092 /* null is implicitly public, so negative is invalid */
1094 1093 if (rev < 0 || rev >= len)
1095 1094 goto failed;
1096 1095 phases[rev] = phase;
1097 1096 if (minrev == -1 || minrev > rev)
1098 1097 minrev = rev;
1099 1098 Py_DECREF(item);
1100 1099 }
1101 1100 Py_DECREF(iterator);
1102 1101 return minrev;
1103 1102 failed:
1104 1103 Py_DECREF(iterator);
1105 1104 Py_DECREF(item);
1106 1105 return -2;
1107 1106 }
1108 1107
1109 1108 static PyObject *compute_phases_map_sets(indexObject *self, PyObject *args)
1110 1109 {
1111 1110 /* 0: public (untracked), 1: draft, 2: secret, 32: archive,
1112 1111 96: internal */
1113 1112 static const char trackedphases[] = {1, 2, 32, 96};
1114 1113 PyObject *roots = Py_None;
1115 1114 PyObject *phasesetsdict = NULL;
1116 1115 PyObject *phasesets[4] = {NULL, NULL, NULL, NULL};
1117 1116 Py_ssize_t len = index_length(self);
1118 1117 char *phases = NULL;
1119 1118 int minphaserev = -1, rev, i;
1120 1119 const int numphases = (int)(sizeof(phasesets) / sizeof(phasesets[0]));
1121 1120
1122 1121 if (!PyArg_ParseTuple(args, "O", &roots))
1123 1122 return NULL;
1124 1123 if (roots == NULL || !PyDict_Check(roots)) {
1125 1124 PyErr_SetString(PyExc_TypeError, "roots must be a dictionary");
1126 1125 return NULL;
1127 1126 }
1128 1127
1129 1128 phases = calloc(len, 1);
1130 1129 if (phases == NULL) {
1131 1130 PyErr_NoMemory();
1132 1131 return NULL;
1133 1132 }
1134 1133
1135 1134 for (i = 0; i < numphases; ++i) {
1136 1135 PyObject *pyphase = PyLong_FromLong(trackedphases[i]);
1137 1136 PyObject *phaseroots = NULL;
1138 1137 if (pyphase == NULL)
1139 1138 goto release;
1140 1139 phaseroots = PyDict_GetItem(roots, pyphase);
1141 1140 Py_DECREF(pyphase);
1142 1141 if (phaseroots == NULL)
1143 1142 continue;
1144 1143 rev = add_roots_get_min(self, phaseroots, phases,
1145 1144 trackedphases[i]);
1146 1145 if (rev == -2)
1147 1146 goto release;
1148 1147 if (rev != -1 && (minphaserev == -1 || rev < minphaserev))
1149 1148 minphaserev = rev;
1150 1149 }
1151 1150
1152 1151 for (i = 0; i < numphases; ++i) {
1153 1152 phasesets[i] = PySet_New(NULL);
1154 1153 if (phasesets[i] == NULL)
1155 1154 goto release;
1156 1155 }
1157 1156
1158 1157 if (minphaserev == -1)
1159 1158 minphaserev = len;
1160 1159 for (rev = minphaserev; rev < len; ++rev) {
1161 1160 PyObject *pyphase = NULL;
1162 1161 PyObject *pyrev = NULL;
1163 1162 int parents[2];
1164 1163 /*
1165 1164 * The parent lookup could be skipped for phaseroots, but
1166 1165 * phase --force would historically not recompute them
1167 1166 * correctly, leaving descendents with a lower phase around.
1168 1167 * As such, unconditionally recompute the phase.
1169 1168 */
1170 1169 if (index_get_parents(self, rev, parents, (int)len - 1) < 0)
1171 1170 goto release;
1172 1171 set_phase_from_parents(phases, parents[0], parents[1], rev);
1173 1172 switch (phases[rev]) {
1174 1173 case 0:
1175 1174 continue;
1176 1175 case 1:
1177 1176 pyphase = phasesets[0];
1178 1177 break;
1179 1178 case 2:
1180 1179 pyphase = phasesets[1];
1181 1180 break;
1182 1181 case 32:
1183 1182 pyphase = phasesets[2];
1184 1183 break;
1185 1184 case 96:
1186 1185 pyphase = phasesets[3];
1187 1186 break;
1188 1187 default:
1189 1188 /* this should never happen since the phase number is
1190 1189 * specified by this function. */
1191 1190 PyErr_SetString(PyExc_SystemError,
1192 1191 "bad phase number in internal list");
1193 1192 goto release;
1194 1193 }
1195 1194 pyrev = PyLong_FromLong(rev);
1196 1195 if (pyrev == NULL)
1197 1196 goto release;
1198 1197 if (PySet_Add(pyphase, pyrev) == -1) {
1199 1198 Py_DECREF(pyrev);
1200 1199 goto release;
1201 1200 }
1202 1201 Py_DECREF(pyrev);
1203 1202 }
1204 1203
1205 1204 phasesetsdict = _dict_new_presized(numphases);
1206 1205 if (phasesetsdict == NULL)
1207 1206 goto release;
1208 1207 for (i = 0; i < numphases; ++i) {
1209 1208 PyObject *pyphase = PyLong_FromLong(trackedphases[i]);
1210 1209 if (pyphase == NULL)
1211 1210 goto release;
1212 1211 if (PyDict_SetItem(phasesetsdict, pyphase, phasesets[i]) ==
1213 1212 -1) {
1214 1213 Py_DECREF(pyphase);
1215 1214 goto release;
1216 1215 }
1217 1216 Py_DECREF(phasesets[i]);
1218 1217 phasesets[i] = NULL;
1219 1218 }
1220 1219
1221 1220 free(phases);
1222 1221 return Py_BuildValue("nN", len, phasesetsdict);
1223 1222
1224 1223 release:
1225 1224 for (i = 0; i < numphases; ++i)
1226 1225 Py_XDECREF(phasesets[i]);
1227 1226 Py_XDECREF(phasesetsdict);
1228 1227
1229 1228 free(phases);
1230 1229 return NULL;
1231 1230 }
1232 1231
1233 1232 static PyObject *index_headrevs(indexObject *self, PyObject *args)
1234 1233 {
1235 1234 Py_ssize_t i, j, len;
1236 1235 char *nothead = NULL;
1237 1236 PyObject *heads = NULL;
1238 1237 PyObject *filter = NULL;
1239 1238 PyObject *filteredrevs = Py_None;
1240 1239
1241 1240 if (!PyArg_ParseTuple(args, "|O", &filteredrevs)) {
1242 1241 return NULL;
1243 1242 }
1244 1243
1245 1244 if (self->headrevs && filteredrevs == self->filteredrevs)
1246 1245 return list_copy(self->headrevs);
1247 1246
1248 1247 Py_DECREF(self->filteredrevs);
1249 1248 self->filteredrevs = filteredrevs;
1250 1249 Py_INCREF(filteredrevs);
1251 1250
1252 1251 if (filteredrevs != Py_None) {
1253 1252 filter = PyObject_GetAttrString(filteredrevs, "__contains__");
1254 1253 if (!filter) {
1255 1254 PyErr_SetString(
1256 1255 PyExc_TypeError,
1257 1256 "filteredrevs has no attribute __contains__");
1258 1257 goto bail;
1259 1258 }
1260 1259 }
1261 1260
1262 1261 len = index_length(self);
1263 1262 heads = PyList_New(0);
1264 1263 if (heads == NULL)
1265 1264 goto bail;
1266 1265 if (len == 0) {
1267 1266 PyObject *nullid = PyLong_FromLong(-1);
1268 1267 if (nullid == NULL || PyList_Append(heads, nullid) == -1) {
1269 1268 Py_XDECREF(nullid);
1270 1269 goto bail;
1271 1270 }
1272 1271 goto done;
1273 1272 }
1274 1273
1275 1274 nothead = calloc(len, 1);
1276 1275 if (nothead == NULL) {
1277 1276 PyErr_NoMemory();
1278 1277 goto bail;
1279 1278 }
1280 1279
1281 1280 for (i = len - 1; i >= 0; i--) {
1282 1281 int isfiltered;
1283 1282 int parents[2];
1284 1283
1285 1284 /* If nothead[i] == 1, it means we've seen an unfiltered child
1286 1285 * of this node already, and therefore this node is not
1287 1286 * filtered. So we can skip the expensive check_filter step.
1288 1287 */
1289 1288 if (nothead[i] != 1) {
1290 1289 isfiltered = check_filter(filter, i);
1291 1290 if (isfiltered == -1) {
1292 1291 PyErr_SetString(PyExc_TypeError,
1293 1292 "unable to check filter");
1294 1293 goto bail;
1295 1294 }
1296 1295
1297 1296 if (isfiltered) {
1298 1297 nothead[i] = 1;
1299 1298 continue;
1300 1299 }
1301 1300 }
1302 1301
1303 1302 if (index_get_parents(self, i, parents, (int)len - 1) < 0)
1304 1303 goto bail;
1305 1304 for (j = 0; j < 2; j++) {
1306 1305 if (parents[j] >= 0)
1307 1306 nothead[parents[j]] = 1;
1308 1307 }
1309 1308 }
1310 1309
1311 1310 for (i = 0; i < len; i++) {
1312 1311 PyObject *head;
1313 1312
1314 1313 if (nothead[i])
1315 1314 continue;
1316 1315 head = PyLong_FromSsize_t(i);
1317 1316 if (head == NULL || PyList_Append(heads, head) == -1) {
1318 1317 Py_XDECREF(head);
1319 1318 goto bail;
1320 1319 }
1321 1320 }
1322 1321
1323 1322 done:
1324 1323 self->headrevs = heads;
1325 1324 Py_XDECREF(filter);
1326 1325 free(nothead);
1327 1326 return list_copy(self->headrevs);
1328 1327 bail:
1329 1328 Py_XDECREF(filter);
1330 1329 Py_XDECREF(heads);
1331 1330 free(nothead);
1332 1331 return NULL;
1333 1332 }
1334 1333
1335 1334 /**
1336 1335 * Obtain the base revision index entry.
1337 1336 *
1338 1337 * Callers must ensure that rev >= 0 or illegal memory access may occur.
1339 1338 */
1340 1339 static inline int index_baserev(indexObject *self, int rev)
1341 1340 {
1342 1341 const char *data;
1343 1342 int result;
1344 1343
1345 1344 data = index_deref(self, rev);
1346 1345 if (data == NULL)
1347 1346 return -2;
1348 1347
1349 1348 if (self->format_version == format_v1) {
1350 1349 result = getbe32(data + entry_v1_offset_base_rev);
1351 1350 } else if (self->format_version == format_v2) {
1352 1351 result = getbe32(data + entry_v2_offset_base_rev);
1353 1352 } else if (self->format_version == format_cl2) {
1354 1353 return rev;
1355 1354 } else {
1356 1355 raise_revlog_error();
1357 1356 return -1;
1358 1357 }
1359 1358
1360 1359 if (result > rev) {
1361 1360 PyErr_Format(
1362 1361 PyExc_ValueError,
1363 1362 "corrupted revlog, revision base above revision: %d, %d",
1364 1363 rev, result);
1365 1364 return -2;
1366 1365 }
1367 1366 if (result < -1) {
1368 1367 PyErr_Format(
1369 1368 PyExc_ValueError,
1370 1369 "corrupted revlog, revision base out of range: %d, %d", rev,
1371 1370 result);
1372 1371 return -2;
1373 1372 }
1374 1373 return result;
1375 1374 }
1376 1375
1377 1376 /**
1378 1377 * Find if a revision is a snapshot or not
1379 1378 *
1380 1379 * Only relevant for sparse-revlog case.
1381 1380 * Callers must ensure that rev is in a valid range.
1382 1381 */
1383 1382 static int index_issnapshotrev(indexObject *self, Py_ssize_t rev)
1384 1383 {
1385 1384 int ps[2];
1386 1385 Py_ssize_t base;
1387 1386 while (rev >= 0) {
1388 1387 base = (Py_ssize_t)index_baserev(self, rev);
1389 1388 if (base == rev) {
1390 1389 base = -1;
1391 1390 }
1392 1391 if (base == -2) {
1393 1392 assert(PyErr_Occurred());
1394 1393 return -1;
1395 1394 }
1396 1395 if (base == -1) {
1397 1396 return 1;
1398 1397 }
1399 1398 if (index_get_parents(self, rev, ps, (int)rev) < 0) {
1400 1399 assert(PyErr_Occurred());
1401 1400 return -1;
1402 1401 };
1403 1402 if (base == ps[0] || base == ps[1]) {
1404 1403 return 0;
1405 1404 }
1406 1405 rev = base;
1407 1406 }
1408 1407 return rev == -1;
1409 1408 }
1410 1409
1411 1410 static PyObject *index_issnapshot(indexObject *self, PyObject *value)
1412 1411 {
1413 1412 long rev;
1414 1413 int issnap;
1415 1414 Py_ssize_t length = index_length(self);
1416 1415
1417 1416 if (!pylong_to_long(value, &rev)) {
1418 1417 return NULL;
1419 1418 }
1420 1419 if (rev < -1 || rev >= length) {
1421 1420 PyErr_Format(PyExc_ValueError, "revlog index out of range: %ld",
1422 1421 rev);
1423 1422 return NULL;
1424 1423 };
1425 1424 issnap = index_issnapshotrev(self, (Py_ssize_t)rev);
1426 1425 if (issnap < 0) {
1427 1426 return NULL;
1428 1427 };
1429 1428 return PyBool_FromLong((long)issnap);
1430 1429 }
1431 1430
1432 1431 static PyObject *index_findsnapshots(indexObject *self, PyObject *args)
1433 1432 {
1434 1433 Py_ssize_t start_rev;
1435 1434 PyObject *cache;
1436 1435 Py_ssize_t base;
1437 1436 Py_ssize_t rev;
1438 1437 PyObject *key = NULL;
1439 1438 PyObject *value = NULL;
1440 1439 const Py_ssize_t length = index_length(self);
1441 1440 if (!PyArg_ParseTuple(args, "O!n", &PyDict_Type, &cache, &start_rev)) {
1442 1441 return NULL;
1443 1442 }
1444 1443 for (rev = start_rev; rev < length; rev++) {
1445 1444 int issnap;
1446 1445 PyObject *allvalues = NULL;
1447 1446 issnap = index_issnapshotrev(self, rev);
1448 1447 if (issnap < 0) {
1449 1448 goto bail;
1450 1449 }
1451 1450 if (issnap == 0) {
1452 1451 continue;
1453 1452 }
1454 1453 base = (Py_ssize_t)index_baserev(self, rev);
1455 1454 if (base == rev) {
1456 1455 base = -1;
1457 1456 }
1458 1457 if (base == -2) {
1459 1458 assert(PyErr_Occurred());
1460 1459 goto bail;
1461 1460 }
1462 1461 key = PyLong_FromSsize_t(base);
1463 1462 allvalues = PyDict_GetItem(cache, key);
1464 1463 if (allvalues == NULL && PyErr_Occurred()) {
1465 1464 goto bail;
1466 1465 }
1467 1466 if (allvalues == NULL) {
1468 1467 int r;
1469 1468 allvalues = PyList_New(0);
1470 1469 if (!allvalues) {
1471 1470 goto bail;
1472 1471 }
1473 1472 r = PyDict_SetItem(cache, key, allvalues);
1474 1473 Py_DECREF(allvalues);
1475 1474 if (r < 0) {
1476 1475 goto bail;
1477 1476 }
1478 1477 }
1479 1478 value = PyLong_FromSsize_t(rev);
1480 1479 if (PyList_Append(allvalues, value)) {
1481 1480 goto bail;
1482 1481 }
1483 1482 Py_CLEAR(key);
1484 1483 Py_CLEAR(value);
1485 1484 }
1486 1485 Py_RETURN_NONE;
1487 1486 bail:
1488 1487 Py_XDECREF(key);
1489 1488 Py_XDECREF(value);
1490 1489 return NULL;
1491 1490 }
1492 1491
1493 1492 static PyObject *index_deltachain(indexObject *self, PyObject *args)
1494 1493 {
1495 1494 int rev, generaldelta;
1496 1495 PyObject *stoparg;
1497 1496 int stoprev, iterrev, baserev = -1;
1498 1497 int stopped;
1499 1498 PyObject *chain = NULL, *result = NULL;
1500 1499 const Py_ssize_t length = index_length(self);
1501 1500
1502 1501 if (!PyArg_ParseTuple(args, "iOi", &rev, &stoparg, &generaldelta)) {
1503 1502 return NULL;
1504 1503 }
1505 1504
1506 1505 if (PyLong_Check(stoparg)) {
1507 1506 stoprev = (int)PyLong_AsLong(stoparg);
1508 1507 if (stoprev == -1 && PyErr_Occurred()) {
1509 1508 return NULL;
1510 1509 }
1511 1510 } else if (stoparg == Py_None) {
1512 1511 stoprev = -2;
1513 1512 } else {
1514 1513 PyErr_SetString(PyExc_ValueError,
1515 1514 "stoprev must be integer or None");
1516 1515 return NULL;
1517 1516 }
1518 1517
1519 1518 if (rev < 0 || rev >= length) {
1520 1519 PyErr_SetString(PyExc_ValueError, "revlog index out of range");
1521 1520 return NULL;
1522 1521 }
1523 1522
1524 1523 chain = PyList_New(0);
1525 1524 if (chain == NULL) {
1526 1525 return NULL;
1527 1526 }
1528 1527
1529 1528 baserev = index_baserev(self, rev);
1530 1529
1531 1530 /* This should never happen. */
1532 1531 if (baserev <= -2) {
1533 1532 /* Error should be set by index_deref() */
1534 1533 assert(PyErr_Occurred());
1535 1534 goto bail;
1536 1535 }
1537 1536
1538 1537 iterrev = rev;
1539 1538
1540 1539 while (iterrev != baserev && iterrev != stoprev) {
1541 1540 PyObject *value = PyLong_FromLong(iterrev);
1542 1541 if (value == NULL) {
1543 1542 goto bail;
1544 1543 }
1545 1544 if (PyList_Append(chain, value)) {
1546 1545 Py_DECREF(value);
1547 1546 goto bail;
1548 1547 }
1549 1548 Py_DECREF(value);
1550 1549
1551 1550 if (generaldelta) {
1552 1551 iterrev = baserev;
1553 1552 } else {
1554 1553 iterrev--;
1555 1554 }
1556 1555
1557 1556 if (iterrev < 0) {
1558 1557 break;
1559 1558 }
1560 1559
1561 1560 if (iterrev >= length) {
1562 1561 PyErr_SetString(PyExc_IndexError,
1563 1562 "revision outside index");
1564 1563 return NULL;
1565 1564 }
1566 1565
1567 1566 baserev = index_baserev(self, iterrev);
1568 1567
1569 1568 /* This should never happen. */
1570 1569 if (baserev <= -2) {
1571 1570 /* Error should be set by index_deref() */
1572 1571 assert(PyErr_Occurred());
1573 1572 goto bail;
1574 1573 }
1575 1574 }
1576 1575
1577 1576 if (iterrev == stoprev) {
1578 1577 stopped = 1;
1579 1578 } else {
1580 1579 PyObject *value = PyLong_FromLong(iterrev);
1581 1580 if (value == NULL) {
1582 1581 goto bail;
1583 1582 }
1584 1583 if (PyList_Append(chain, value)) {
1585 1584 Py_DECREF(value);
1586 1585 goto bail;
1587 1586 }
1588 1587 Py_DECREF(value);
1589 1588
1590 1589 stopped = 0;
1591 1590 }
1592 1591
1593 1592 if (PyList_Reverse(chain)) {
1594 1593 goto bail;
1595 1594 }
1596 1595
1597 1596 result = Py_BuildValue("OO", chain, stopped ? Py_True : Py_False);
1598 1597 Py_DECREF(chain);
1599 1598 return result;
1600 1599
1601 1600 bail:
1602 1601 Py_DECREF(chain);
1603 1602 return NULL;
1604 1603 }
1605 1604
1606 1605 static inline int64_t
1607 1606 index_segment_span(indexObject *self, Py_ssize_t start_rev, Py_ssize_t end_rev)
1608 1607 {
1609 1608 int64_t start_offset;
1610 1609 int64_t end_offset;
1611 1610 int end_size;
1612 1611 start_offset = index_get_start(self, start_rev);
1613 1612 if (start_offset < 0) {
1614 1613 return -1;
1615 1614 }
1616 1615 end_offset = index_get_start(self, end_rev);
1617 1616 if (end_offset < 0) {
1618 1617 return -1;
1619 1618 }
1620 1619 end_size = index_get_length(self, end_rev);
1621 1620 if (end_size < 0) {
1622 1621 return -1;
1623 1622 }
1624 1623 if (end_offset < start_offset) {
1625 1624 PyErr_Format(PyExc_ValueError,
1626 1625 "corrupted revlog index: inconsistent offset "
1627 1626 "between revisions (%zd) and (%zd)",
1628 1627 start_rev, end_rev);
1629 1628 return -1;
1630 1629 }
1631 1630 return (end_offset - start_offset) + (int64_t)end_size;
1632 1631 }
1633 1632
1634 1633 /* returns endidx so that revs[startidx:endidx] has no empty trailing revs */
1635 1634 static Py_ssize_t trim_endidx(indexObject *self, const Py_ssize_t *revs,
1636 1635 Py_ssize_t startidx, Py_ssize_t endidx)
1637 1636 {
1638 1637 int length;
1639 1638 while (endidx > 1 && endidx > startidx) {
1640 1639 length = index_get_length(self, revs[endidx - 1]);
1641 1640 if (length < 0) {
1642 1641 return -1;
1643 1642 }
1644 1643 if (length != 0) {
1645 1644 break;
1646 1645 }
1647 1646 endidx -= 1;
1648 1647 }
1649 1648 return endidx;
1650 1649 }
1651 1650
1652 1651 struct Gap {
1653 1652 int64_t size;
1654 1653 Py_ssize_t idx;
1655 1654 };
1656 1655
1657 1656 static int gap_compare(const void *left, const void *right)
1658 1657 {
1659 1658 const struct Gap *l_left = ((const struct Gap *)left);
1660 1659 const struct Gap *l_right = ((const struct Gap *)right);
1661 1660 if (l_left->size < l_right->size) {
1662 1661 return -1;
1663 1662 } else if (l_left->size > l_right->size) {
1664 1663 return 1;
1665 1664 }
1666 1665 return 0;
1667 1666 }
1668 1667 static int Py_ssize_t_compare(const void *left, const void *right)
1669 1668 {
1670 1669 const Py_ssize_t l_left = *(const Py_ssize_t *)left;
1671 1670 const Py_ssize_t l_right = *(const Py_ssize_t *)right;
1672 1671 if (l_left < l_right) {
1673 1672 return -1;
1674 1673 } else if (l_left > l_right) {
1675 1674 return 1;
1676 1675 }
1677 1676 return 0;
1678 1677 }
1679 1678
1680 1679 static PyObject *index_slicechunktodensity(indexObject *self, PyObject *args)
1681 1680 {
1682 1681 /* method arguments */
1683 1682 PyObject *list_revs = NULL; /* revisions in the chain */
1684 1683 double targetdensity = 0; /* min density to achieve */
1685 1684 Py_ssize_t mingapsize = 0; /* threshold to ignore gaps */
1686 1685
1687 1686 /* other core variables */
1688 1687 Py_ssize_t idxlen = index_length(self);
1689 1688 Py_ssize_t i; /* used for various iteration */
1690 1689 PyObject *result = NULL; /* the final return of the function */
1691 1690
1692 1691 /* generic information about the delta chain being slice */
1693 1692 Py_ssize_t num_revs = 0; /* size of the full delta chain */
1694 1693 Py_ssize_t *revs = NULL; /* native array of revision in the chain */
1695 1694 int64_t chainpayload = 0; /* sum of all delta in the chain */
1696 1695 int64_t deltachainspan = 0; /* distance from first byte to last byte */
1697 1696
1698 1697 /* variable used for slicing the delta chain */
1699 1698 int64_t readdata = 0; /* amount of data currently planned to be read */
1700 1699 double density = 0; /* ration of payload data compared to read ones */
1701 1700 int64_t previous_end;
1702 1701 struct Gap *gaps = NULL; /* array of notable gap in the chain */
1703 1702 Py_ssize_t num_gaps =
1704 1703 0; /* total number of notable gap recorded so far */
1705 1704 Py_ssize_t *selected_indices = NULL; /* indices of gap skipped over */
1706 1705 Py_ssize_t num_selected = 0; /* number of gaps skipped */
1707 1706 PyObject *chunk = NULL; /* individual slice */
1708 1707 PyObject *allchunks = NULL; /* all slices */
1709 1708 Py_ssize_t previdx;
1710 1709
1711 1710 /* parsing argument */
1712 1711 if (!PyArg_ParseTuple(args, "O!dn", &PyList_Type, &list_revs,
1713 1712 &targetdensity, &mingapsize)) {
1714 1713 goto bail;
1715 1714 }
1716 1715
1717 1716 /* If the delta chain contains a single element, we do not need slicing
1718 1717 */
1719 1718 num_revs = PyList_GET_SIZE(list_revs);
1720 1719 if (num_revs <= 1) {
1721 1720 result = PyTuple_Pack(1, list_revs);
1722 1721 goto done;
1723 1722 }
1724 1723
1725 1724 /* Turn the python list into a native integer array (for efficiency) */
1726 1725 revs = (Py_ssize_t *)calloc(num_revs, sizeof(Py_ssize_t));
1727 1726 if (revs == NULL) {
1728 1727 PyErr_NoMemory();
1729 1728 goto bail;
1730 1729 }
1731 1730 for (i = 0; i < num_revs; i++) {
1732 1731 Py_ssize_t revnum =
1733 1732 PyLong_AsLong(PyList_GET_ITEM(list_revs, i));
1734 1733 if (revnum == -1 && PyErr_Occurred()) {
1735 1734 goto bail;
1736 1735 }
1737 1736 if (revnum < nullrev || revnum >= idxlen) {
1738 1737 PyErr_Format(PyExc_IndexError,
1739 1738 "index out of range: %zd", revnum);
1740 1739 goto bail;
1741 1740 }
1742 1741 revs[i] = revnum;
1743 1742 }
1744 1743
1745 1744 /* Compute and check various property of the unsliced delta chain */
1746 1745 deltachainspan = index_segment_span(self, revs[0], revs[num_revs - 1]);
1747 1746 if (deltachainspan < 0) {
1748 1747 goto bail;
1749 1748 }
1750 1749
1751 1750 if (deltachainspan <= mingapsize) {
1752 1751 result = PyTuple_Pack(1, list_revs);
1753 1752 goto done;
1754 1753 }
1755 1754 chainpayload = 0;
1756 1755 for (i = 0; i < num_revs; i++) {
1757 1756 int tmp = index_get_length(self, revs[i]);
1758 1757 if (tmp < 0) {
1759 1758 goto bail;
1760 1759 }
1761 1760 chainpayload += tmp;
1762 1761 }
1763 1762
1764 1763 readdata = deltachainspan;
1765 1764 density = 1.0;
1766 1765
1767 1766 if (0 < deltachainspan) {
1768 1767 density = (double)chainpayload / (double)deltachainspan;
1769 1768 }
1770 1769
1771 1770 if (density >= targetdensity) {
1772 1771 result = PyTuple_Pack(1, list_revs);
1773 1772 goto done;
1774 1773 }
1775 1774
1776 1775 /* if chain is too sparse, look for relevant gaps */
1777 1776 gaps = (struct Gap *)calloc(num_revs, sizeof(struct Gap));
1778 1777 if (gaps == NULL) {
1779 1778 PyErr_NoMemory();
1780 1779 goto bail;
1781 1780 }
1782 1781
1783 1782 previous_end = -1;
1784 1783 for (i = 0; i < num_revs; i++) {
1785 1784 int64_t revstart;
1786 1785 int revsize;
1787 1786 revstart = index_get_start(self, revs[i]);
1788 1787 if (revstart < 0) {
1789 1788 goto bail;
1790 1789 };
1791 1790 revsize = index_get_length(self, revs[i]);
1792 1791 if (revsize < 0) {
1793 1792 goto bail;
1794 1793 };
1795 1794 if (revsize == 0) {
1796 1795 continue;
1797 1796 }
1798 1797 if (previous_end >= 0) {
1799 1798 int64_t gapsize = revstart - previous_end;
1800 1799 if (gapsize > mingapsize) {
1801 1800 gaps[num_gaps].size = gapsize;
1802 1801 gaps[num_gaps].idx = i;
1803 1802 num_gaps += 1;
1804 1803 }
1805 1804 }
1806 1805 previous_end = revstart + revsize;
1807 1806 }
1808 1807 if (num_gaps == 0) {
1809 1808 result = PyTuple_Pack(1, list_revs);
1810 1809 goto done;
1811 1810 }
1812 1811 qsort(gaps, num_gaps, sizeof(struct Gap), &gap_compare);
1813 1812
1814 1813 /* Slice the largest gap first, they improve the density the most */
1815 1814 selected_indices =
1816 1815 (Py_ssize_t *)malloc((num_gaps + 1) * sizeof(Py_ssize_t));
1817 1816 if (selected_indices == NULL) {
1818 1817 PyErr_NoMemory();
1819 1818 goto bail;
1820 1819 }
1821 1820
1822 1821 for (i = num_gaps - 1; i >= 0; i--) {
1823 1822 selected_indices[num_selected] = gaps[i].idx;
1824 1823 readdata -= gaps[i].size;
1825 1824 num_selected += 1;
1826 1825 if (readdata <= 0) {
1827 1826 density = 1.0;
1828 1827 } else {
1829 1828 density = (double)chainpayload / (double)readdata;
1830 1829 }
1831 1830 if (density >= targetdensity) {
1832 1831 break;
1833 1832 }
1834 1833 }
1835 1834 qsort(selected_indices, num_selected, sizeof(Py_ssize_t),
1836 1835 &Py_ssize_t_compare);
1837 1836
1838 1837 /* create the resulting slice */
1839 1838 allchunks = PyList_New(0);
1840 1839 if (allchunks == NULL) {
1841 1840 goto bail;
1842 1841 }
1843 1842 previdx = 0;
1844 1843 selected_indices[num_selected] = num_revs;
1845 1844 for (i = 0; i <= num_selected; i++) {
1846 1845 Py_ssize_t idx = selected_indices[i];
1847 1846 Py_ssize_t endidx = trim_endidx(self, revs, previdx, idx);
1848 1847 if (endidx < 0) {
1849 1848 goto bail;
1850 1849 }
1851 1850 if (previdx < endidx) {
1852 1851 chunk = PyList_GetSlice(list_revs, previdx, endidx);
1853 1852 if (chunk == NULL) {
1854 1853 goto bail;
1855 1854 }
1856 1855 if (PyList_Append(allchunks, chunk) == -1) {
1857 1856 goto bail;
1858 1857 }
1859 1858 Py_DECREF(chunk);
1860 1859 chunk = NULL;
1861 1860 }
1862 1861 previdx = idx;
1863 1862 }
1864 1863 result = allchunks;
1865 1864 goto done;
1866 1865
1867 1866 bail:
1868 1867 Py_XDECREF(allchunks);
1869 1868 Py_XDECREF(chunk);
1870 1869 done:
1871 1870 free(revs);
1872 1871 free(gaps);
1873 1872 free(selected_indices);
1874 1873 return result;
1875 1874 }
1876 1875
1877 1876 static inline int nt_level(const char *node, Py_ssize_t level)
1878 1877 {
1879 1878 int v = node[level >> 1];
1880 1879 if (!(level & 1))
1881 1880 v >>= 4;
1882 1881 return v & 0xf;
1883 1882 }
1884 1883
1885 1884 /*
1886 1885 * Return values:
1887 1886 *
1888 1887 * -4: match is ambiguous (multiple candidates)
1889 1888 * -2: not found
1890 1889 * rest: valid rev
1891 1890 */
1892 1891 static int nt_find(nodetree *self, const char *node, Py_ssize_t nodelen,
1893 1892 int hex)
1894 1893 {
1895 1894 int (*getnybble)(const char *, Py_ssize_t) = hex ? hexdigit : nt_level;
1896 1895 int level, maxlevel, off;
1897 1896
1898 1897 /* If the input is binary, do a fast check for the nullid first. */
1899 1898 if (!hex && nodelen == self->nodelen && node[0] == '\0' &&
1900 1899 node[1] == '\0' && memcmp(node, nullid, self->nodelen) == 0)
1901 1900 return -1;
1902 1901
1903 1902 if (hex)
1904 1903 maxlevel = nodelen;
1905 1904 else
1906 1905 maxlevel = 2 * nodelen;
1907 1906 if (maxlevel > 2 * self->nodelen)
1908 1907 maxlevel = 2 * self->nodelen;
1909 1908
1910 1909 for (level = off = 0; level < maxlevel; level++) {
1911 1910 int k = getnybble(node, level);
1912 1911 nodetreenode *n = &self->nodes[off];
1913 1912 int v = n->children[k];
1914 1913
1915 1914 if (v < 0) {
1916 1915 const char *n;
1917 1916 Py_ssize_t i;
1918 1917
1919 1918 v = -(v + 2);
1920 1919 n = index_node(self->index, v);
1921 1920 if (n == NULL)
1922 1921 return -2;
1923 1922 for (i = level; i < maxlevel; i++)
1924 1923 if (getnybble(node, i) != nt_level(n, i))
1925 1924 return -2;
1926 1925 return v;
1927 1926 }
1928 1927 if (v == 0)
1929 1928 return -2;
1930 1929 off = v;
1931 1930 }
1932 1931 /* multiple matches against an ambiguous prefix */
1933 1932 return -4;
1934 1933 }
1935 1934
1936 1935 static int nt_new(nodetree *self)
1937 1936 {
1938 1937 if (self->length == self->capacity) {
1939 1938 size_t newcapacity;
1940 1939 nodetreenode *newnodes;
1941 1940 newcapacity = self->capacity * 2;
1942 1941 if (newcapacity >= SIZE_MAX / sizeof(nodetreenode)) {
1943 1942 PyErr_SetString(PyExc_MemoryError,
1944 1943 "overflow in nt_new");
1945 1944 return -1;
1946 1945 }
1947 1946 newnodes =
1948 1947 realloc(self->nodes, newcapacity * sizeof(nodetreenode));
1949 1948 if (newnodes == NULL) {
1950 1949 PyErr_SetString(PyExc_MemoryError, "out of memory");
1951 1950 return -1;
1952 1951 }
1953 1952 self->capacity = newcapacity;
1954 1953 self->nodes = newnodes;
1955 1954 memset(&self->nodes[self->length], 0,
1956 1955 sizeof(nodetreenode) * (self->capacity - self->length));
1957 1956 }
1958 1957 return self->length++;
1959 1958 }
1960 1959
1961 1960 static int nt_insert(nodetree *self, const char *node, int rev)
1962 1961 {
1963 1962 int level = 0;
1964 1963 int off = 0;
1965 1964
1966 1965 while (level < 2 * self->nodelen) {
1967 1966 int k = nt_level(node, level);
1968 1967 nodetreenode *n;
1969 1968 int v;
1970 1969
1971 1970 n = &self->nodes[off];
1972 1971 v = n->children[k];
1973 1972
1974 1973 if (v == 0) {
1975 1974 n->children[k] = -rev - 2;
1976 1975 return 0;
1977 1976 }
1978 1977 if (v < 0) {
1979 1978 const char *oldnode =
1980 1979 index_node_existing(self->index, -(v + 2));
1981 1980 int noff;
1982 1981
1983 1982 if (oldnode == NULL)
1984 1983 return -1;
1985 1984 if (!memcmp(oldnode, node, self->nodelen)) {
1986 1985 n->children[k] = -rev - 2;
1987 1986 return 0;
1988 1987 }
1989 1988 noff = nt_new(self);
1990 1989 if (noff == -1)
1991 1990 return -1;
1992 1991 /* self->nodes may have been changed by realloc */
1993 1992 self->nodes[off].children[k] = noff;
1994 1993 off = noff;
1995 1994 n = &self->nodes[off];
1996 1995 n->children[nt_level(oldnode, ++level)] = v;
1997 1996 if (level > self->depth)
1998 1997 self->depth = level;
1999 1998 self->splits += 1;
2000 1999 } else {
2001 2000 level += 1;
2002 2001 off = v;
2003 2002 }
2004 2003 }
2005 2004
2006 2005 return -1;
2007 2006 }
2008 2007
2009 2008 static PyObject *ntobj_insert(nodetreeObject *self, PyObject *args)
2010 2009 {
2011 2010 Py_ssize_t rev;
2012 2011 const char *node;
2013 2012 Py_ssize_t length;
2014 2013 if (!PyArg_ParseTuple(args, "n", &rev))
2015 2014 return NULL;
2016 2015 length = index_length(self->nt.index);
2017 2016 if (rev < 0 || rev >= length) {
2018 2017 PyErr_SetString(PyExc_ValueError, "revlog index out of range");
2019 2018 return NULL;
2020 2019 }
2021 2020 node = index_node_existing(self->nt.index, rev);
2022 2021 if (nt_insert(&self->nt, node, (int)rev) == -1)
2023 2022 return NULL;
2024 2023 Py_RETURN_NONE;
2025 2024 }
2026 2025
2027 2026 static int nt_delete_node(nodetree *self, const char *node)
2028 2027 {
2029 2028 /* rev==-2 happens to get encoded as 0, which is interpreted as not set
2030 2029 */
2031 2030 return nt_insert(self, node, -2);
2032 2031 }
2033 2032
2034 2033 static int nt_init(nodetree *self, indexObject *index, unsigned capacity)
2035 2034 {
2036 2035 /* Initialize before overflow-checking to avoid nt_dealloc() crash. */
2037 2036 self->nodes = NULL;
2038 2037
2039 2038 self->index = index;
2040 2039 /* The input capacity is in terms of revisions, while the field is in
2041 2040 * terms of nodetree nodes. */
2042 2041 self->capacity = (capacity < 4 ? 4 : capacity / 2);
2043 2042 self->nodelen = index->nodelen;
2044 2043 self->depth = 0;
2045 2044 self->splits = 0;
2046 2045 if (self->capacity > SIZE_MAX / sizeof(nodetreenode)) {
2047 2046 PyErr_SetString(PyExc_ValueError, "overflow in init_nt");
2048 2047 return -1;
2049 2048 }
2050 2049 self->nodes = calloc(self->capacity, sizeof(nodetreenode));
2051 2050 if (self->nodes == NULL) {
2052 2051 PyErr_NoMemory();
2053 2052 return -1;
2054 2053 }
2055 2054 self->length = 1;
2056 2055 return 0;
2057 2056 }
2058 2057
2059 2058 static int ntobj_init(nodetreeObject *self, PyObject *args)
2060 2059 {
2061 2060 PyObject *index;
2062 2061 unsigned capacity;
2063 2062 if (!PyArg_ParseTuple(args, "O!I", &HgRevlogIndex_Type, &index,
2064 2063 &capacity))
2065 2064 return -1;
2066 2065 Py_INCREF(index);
2067 2066 return nt_init(&self->nt, (indexObject *)index, capacity);
2068 2067 }
2069 2068
2070 2069 static int nt_partialmatch(nodetree *self, const char *node, Py_ssize_t nodelen)
2071 2070 {
2072 2071 return nt_find(self, node, nodelen, 1);
2073 2072 }
2074 2073
2075 2074 /*
2076 2075 * Find the length of the shortest unique prefix of node.
2077 2076 *
2078 2077 * Return values:
2079 2078 *
2080 2079 * -3: error (exception set)
2081 2080 * -2: not found (no exception set)
2082 2081 * rest: length of shortest prefix
2083 2082 */
2084 2083 static int nt_shortest(nodetree *self, const char *node)
2085 2084 {
2086 2085 int level, off;
2087 2086
2088 2087 for (level = off = 0; level < 2 * self->nodelen; level++) {
2089 2088 int k, v;
2090 2089 nodetreenode *n = &self->nodes[off];
2091 2090 k = nt_level(node, level);
2092 2091 v = n->children[k];
2093 2092 if (v < 0) {
2094 2093 const char *n;
2095 2094 v = -(v + 2);
2096 2095 n = index_node_existing(self->index, v);
2097 2096 if (n == NULL)
2098 2097 return -3;
2099 2098 if (memcmp(node, n, self->nodelen) != 0)
2100 2099 /*
2101 2100 * Found a unique prefix, but it wasn't for the
2102 2101 * requested node (i.e the requested node does
2103 2102 * not exist).
2104 2103 */
2105 2104 return -2;
2106 2105 return level + 1;
2107 2106 }
2108 2107 if (v == 0)
2109 2108 return -2;
2110 2109 off = v;
2111 2110 }
2112 2111 /*
2113 2112 * The node was still not unique after 40 hex digits, so this won't
2114 2113 * happen. Also, if we get here, then there's a programming error in
2115 2114 * this file that made us insert a node longer than 40 hex digits.
2116 2115 */
2117 2116 PyErr_SetString(PyExc_Exception, "broken node tree");
2118 2117 return -3;
2119 2118 }
2120 2119
2121 2120 static PyObject *ntobj_shortest(nodetreeObject *self, PyObject *args)
2122 2121 {
2123 2122 PyObject *val;
2124 2123 char *node;
2125 2124 int length;
2126 2125
2127 2126 if (!PyArg_ParseTuple(args, "O", &val))
2128 2127 return NULL;
2129 2128 if (node_check(self->nt.nodelen, val, &node) == -1)
2130 2129 return NULL;
2131 2130
2132 2131 length = nt_shortest(&self->nt, node);
2133 2132 if (length == -3)
2134 2133 return NULL;
2135 2134 if (length == -2) {
2136 2135 raise_revlog_error();
2137 2136 return NULL;
2138 2137 }
2139 2138 return PyLong_FromLong(length);
2140 2139 }
2141 2140
2142 2141 static void nt_dealloc(nodetree *self)
2143 2142 {
2144 2143 free(self->nodes);
2145 2144 self->nodes = NULL;
2146 2145 }
2147 2146
2148 2147 static void ntobj_dealloc(nodetreeObject *self)
2149 2148 {
2150 2149 Py_XDECREF(self->nt.index);
2151 2150 nt_dealloc(&self->nt);
2152 2151 PyObject_Del(self);
2153 2152 }
2154 2153
2155 2154 static PyMethodDef ntobj_methods[] = {
2156 2155 {"insert", (PyCFunction)ntobj_insert, METH_VARARGS,
2157 2156 "insert an index entry"},
2158 2157 {"shortest", (PyCFunction)ntobj_shortest, METH_VARARGS,
2159 2158 "find length of shortest hex nodeid of a binary ID"},
2160 2159 {NULL} /* Sentinel */
2161 2160 };
2162 2161
2163 2162 static PyTypeObject nodetreeType = {
2164 2163 PyVarObject_HEAD_INIT(NULL, 0) /* header */
2165 2164 "parsers.nodetree", /* tp_name */
2166 2165 sizeof(nodetreeObject), /* tp_basicsize */
2167 2166 0, /* tp_itemsize */
2168 2167 (destructor)ntobj_dealloc, /* tp_dealloc */
2169 2168 0, /* tp_print */
2170 2169 0, /* tp_getattr */
2171 2170 0, /* tp_setattr */
2172 2171 0, /* tp_compare */
2173 2172 0, /* tp_repr */
2174 2173 0, /* tp_as_number */
2175 2174 0, /* tp_as_sequence */
2176 2175 0, /* tp_as_mapping */
2177 2176 0, /* tp_hash */
2178 2177 0, /* tp_call */
2179 2178 0, /* tp_str */
2180 2179 0, /* tp_getattro */
2181 2180 0, /* tp_setattro */
2182 2181 0, /* tp_as_buffer */
2183 2182 Py_TPFLAGS_DEFAULT, /* tp_flags */
2184 2183 "nodetree", /* tp_doc */
2185 2184 0, /* tp_traverse */
2186 2185 0, /* tp_clear */
2187 2186 0, /* tp_richcompare */
2188 2187 0, /* tp_weaklistoffset */
2189 2188 0, /* tp_iter */
2190 2189 0, /* tp_iternext */
2191 2190 ntobj_methods, /* tp_methods */
2192 2191 0, /* tp_members */
2193 2192 0, /* tp_getset */
2194 2193 0, /* tp_base */
2195 2194 0, /* tp_dict */
2196 2195 0, /* tp_descr_get */
2197 2196 0, /* tp_descr_set */
2198 2197 0, /* tp_dictoffset */
2199 2198 (initproc)ntobj_init, /* tp_init */
2200 2199 0, /* tp_alloc */
2201 2200 };
2202 2201
2203 2202 static int index_init_nt(indexObject *self)
2204 2203 {
2205 2204 if (!self->ntinitialized) {
2206 2205 if (nt_init(&self->nt, self, (int)self->length) == -1) {
2207 2206 nt_dealloc(&self->nt);
2208 2207 return -1;
2209 2208 }
2210 2209 if (nt_insert(&self->nt, nullid, -1) == -1) {
2211 2210 nt_dealloc(&self->nt);
2212 2211 return -1;
2213 2212 }
2214 2213 self->ntinitialized = 1;
2215 2214 self->ntrev = (int)index_length(self);
2216 2215 self->ntlookups = 1;
2217 2216 self->ntmisses = 0;
2218 2217 }
2219 2218 return 0;
2220 2219 }
2221 2220
2222 2221 /*
2223 2222 * Return values:
2224 2223 *
2225 2224 * -3: error (exception set)
2226 2225 * -2: not found (no exception set)
2227 2226 * rest: valid rev
2228 2227 */
2229 2228 static int index_find_node(indexObject *self, const char *node)
2230 2229 {
2231 2230 int rev;
2232 2231
2233 2232 if (index_init_nt(self) == -1)
2234 2233 return -3;
2235 2234
2236 2235 self->ntlookups++;
2237 2236 rev = nt_find(&self->nt, node, self->nodelen, 0);
2238 2237 if (rev >= -1)
2239 2238 return rev;
2240 2239
2241 2240 /*
2242 2241 * For the first handful of lookups, we scan the entire index,
2243 2242 * and cache only the matching nodes. This optimizes for cases
2244 2243 * like "hg tip", where only a few nodes are accessed.
2245 2244 *
2246 2245 * After that, we cache every node we visit, using a single
2247 2246 * scan amortized over multiple lookups. This gives the best
2248 2247 * bulk performance, e.g. for "hg log".
2249 2248 */
2250 2249 if (self->ntmisses++ < 4) {
2251 2250 for (rev = self->ntrev - 1; rev >= 0; rev--) {
2252 2251 const char *n = index_node_existing(self, rev);
2253 2252 if (n == NULL)
2254 2253 return -3;
2255 2254 if (memcmp(node, n, self->nodelen) == 0) {
2256 2255 if (nt_insert(&self->nt, n, rev) == -1)
2257 2256 return -3;
2258 2257 break;
2259 2258 }
2260 2259 }
2261 2260 } else {
2262 2261 for (rev = self->ntrev - 1; rev >= 0; rev--) {
2263 2262 const char *n = index_node_existing(self, rev);
2264 2263 if (n == NULL)
2265 2264 return -3;
2266 2265 if (nt_insert(&self->nt, n, rev) == -1) {
2267 2266 self->ntrev = rev + 1;
2268 2267 return -3;
2269 2268 }
2270 2269 if (memcmp(node, n, self->nodelen) == 0) {
2271 2270 break;
2272 2271 }
2273 2272 }
2274 2273 self->ntrev = rev;
2275 2274 }
2276 2275
2277 2276 if (rev >= 0)
2278 2277 return rev;
2279 2278 return -2;
2280 2279 }
2281 2280
2282 2281 static PyObject *index_getitem(indexObject *self, PyObject *value)
2283 2282 {
2284 2283 char *node;
2285 2284 int rev;
2286 2285
2287 2286 if (PyLong_Check(value)) {
2288 2287 long idx;
2289 2288 if (!pylong_to_long(value, &idx)) {
2290 2289 return NULL;
2291 2290 }
2292 2291 return index_get(self, idx);
2293 2292 }
2294 2293
2295 2294 if (node_check(self->nodelen, value, &node) == -1)
2296 2295 return NULL;
2297 2296 rev = index_find_node(self, node);
2298 2297 if (rev >= -1)
2299 2298 return PyLong_FromLong(rev);
2300 2299 if (rev == -2)
2301 2300 raise_revlog_error();
2302 2301 return NULL;
2303 2302 }
2304 2303
2305 2304 /*
2306 2305 * Fully populate the radix tree.
2307 2306 */
2308 2307 static int index_populate_nt(indexObject *self)
2309 2308 {
2310 2309 int rev;
2311 2310 if (self->ntrev > 0) {
2312 2311 for (rev = self->ntrev - 1; rev >= 0; rev--) {
2313 2312 const char *n = index_node_existing(self, rev);
2314 2313 if (n == NULL)
2315 2314 return -1;
2316 2315 if (nt_insert(&self->nt, n, rev) == -1)
2317 2316 return -1;
2318 2317 }
2319 2318 self->ntrev = -1;
2320 2319 }
2321 2320 return 0;
2322 2321 }
2323 2322
2324 2323 static PyObject *index_partialmatch(indexObject *self, PyObject *args)
2325 2324 {
2326 2325 const char *fullnode;
2327 2326 Py_ssize_t nodelen;
2328 2327 char *node;
2329 2328 int rev, i;
2330 2329
2331 2330 if (!PyArg_ParseTuple(args, "y#", &node, &nodelen))
2332 2331 return NULL;
2333 2332
2334 2333 if (nodelen < 1) {
2335 2334 PyErr_SetString(PyExc_ValueError, "key too short");
2336 2335 return NULL;
2337 2336 }
2338 2337
2339 2338 if (nodelen > 2 * self->nodelen) {
2340 2339 PyErr_SetString(PyExc_ValueError, "key too long");
2341 2340 return NULL;
2342 2341 }
2343 2342
2344 2343 for (i = 0; i < nodelen; i++)
2345 2344 hexdigit(node, i);
2346 2345 if (PyErr_Occurred()) {
2347 2346 /* input contains non-hex characters */
2348 2347 PyErr_Clear();
2349 2348 Py_RETURN_NONE;
2350 2349 }
2351 2350
2352 2351 if (index_init_nt(self) == -1)
2353 2352 return NULL;
2354 2353 if (index_populate_nt(self) == -1)
2355 2354 return NULL;
2356 2355 rev = nt_partialmatch(&self->nt, node, nodelen);
2357 2356
2358 2357 switch (rev) {
2359 2358 case -4:
2360 2359 raise_revlog_error();
2361 2360 return NULL;
2362 2361 case -2:
2363 2362 Py_RETURN_NONE;
2364 2363 case -1:
2365 2364 return PyBytes_FromStringAndSize(nullid, self->nodelen);
2366 2365 }
2367 2366
2368 2367 fullnode = index_node_existing(self, rev);
2369 2368 if (fullnode == NULL) {
2370 2369 return NULL;
2371 2370 }
2372 2371 return PyBytes_FromStringAndSize(fullnode, self->nodelen);
2373 2372 }
2374 2373
2375 2374 static PyObject *index_shortest(indexObject *self, PyObject *args)
2376 2375 {
2377 2376 PyObject *val;
2378 2377 char *node;
2379 2378 int length;
2380 2379
2381 2380 if (!PyArg_ParseTuple(args, "O", &val))
2382 2381 return NULL;
2383 2382 if (node_check(self->nodelen, val, &node) == -1)
2384 2383 return NULL;
2385 2384
2386 2385 self->ntlookups++;
2387 2386 if (index_init_nt(self) == -1)
2388 2387 return NULL;
2389 2388 if (index_populate_nt(self) == -1)
2390 2389 return NULL;
2391 2390 length = nt_shortest(&self->nt, node);
2392 2391 if (length == -3)
2393 2392 return NULL;
2394 2393 if (length == -2) {
2395 2394 raise_revlog_error();
2396 2395 return NULL;
2397 2396 }
2398 2397 return PyLong_FromLong(length);
2399 2398 }
2400 2399
2401 2400 static PyObject *index_m_get(indexObject *self, PyObject *args)
2402 2401 {
2403 2402 PyObject *val;
2404 2403 char *node;
2405 2404 int rev;
2406 2405
2407 2406 if (!PyArg_ParseTuple(args, "O", &val))
2408 2407 return NULL;
2409 2408 if (node_check(self->nodelen, val, &node) == -1)
2410 2409 return NULL;
2411 2410 rev = index_find_node(self, node);
2412 2411 if (rev == -3)
2413 2412 return NULL;
2414 2413 if (rev == -2)
2415 2414 Py_RETURN_NONE;
2416 2415 return PyLong_FromLong(rev);
2417 2416 }
2418 2417
2419 2418 static int index_contains(indexObject *self, PyObject *value)
2420 2419 {
2421 2420 char *node;
2422 2421
2423 2422 if (PyLong_Check(value)) {
2424 2423 long rev;
2425 2424 if (!pylong_to_long(value, &rev)) {
2426 2425 return -1;
2427 2426 }
2428 2427 return rev >= -1 && rev < index_length(self);
2429 2428 }
2430 2429
2431 2430 if (node_check(self->nodelen, value, &node) == -1)
2432 2431 return -1;
2433 2432
2434 2433 switch (index_find_node(self, node)) {
2435 2434 case -3:
2436 2435 return -1;
2437 2436 case -2:
2438 2437 return 0;
2439 2438 default:
2440 2439 return 1;
2441 2440 }
2442 2441 }
2443 2442
2444 2443 static PyObject *index_m_has_node(indexObject *self, PyObject *args)
2445 2444 {
2446 2445 int ret = index_contains(self, args);
2447 2446 if (ret < 0)
2448 2447 return NULL;
2449 2448 return PyBool_FromLong((long)ret);
2450 2449 }
2451 2450
2452 2451 static PyObject *index_m_rev(indexObject *self, PyObject *val)
2453 2452 {
2454 2453 char *node;
2455 2454 int rev;
2456 2455
2457 2456 if (node_check(self->nodelen, val, &node) == -1)
2458 2457 return NULL;
2459 2458 rev = index_find_node(self, node);
2460 2459 if (rev >= -1)
2461 2460 return PyLong_FromLong(rev);
2462 2461 if (rev == -2)
2463 2462 raise_revlog_error();
2464 2463 return NULL;
2465 2464 }
2466 2465
2467 2466 typedef uint64_t bitmask;
2468 2467
2469 2468 /*
2470 2469 * Given a disjoint set of revs, return all candidates for the
2471 2470 * greatest common ancestor. In revset notation, this is the set
2472 2471 * "heads(::a and ::b and ...)"
2473 2472 */
2474 2473 static PyObject *find_gca_candidates(indexObject *self, const int *revs,
2475 2474 int revcount)
2476 2475 {
2477 2476 const bitmask allseen = (1ull << revcount) - 1;
2478 2477 const bitmask poison = 1ull << revcount;
2479 2478 PyObject *gca = PyList_New(0);
2480 2479 int i, v, interesting;
2481 2480 int maxrev = -1;
2482 2481 bitmask sp;
2483 2482 bitmask *seen;
2484 2483
2485 2484 if (gca == NULL)
2486 2485 return PyErr_NoMemory();
2487 2486
2488 2487 for (i = 0; i < revcount; i++) {
2489 2488 if (revs[i] > maxrev)
2490 2489 maxrev = revs[i];
2491 2490 }
2492 2491
2493 2492 seen = calloc(sizeof(*seen), maxrev + 1);
2494 2493 if (seen == NULL) {
2495 2494 Py_DECREF(gca);
2496 2495 return PyErr_NoMemory();
2497 2496 }
2498 2497
2499 2498 for (i = 0; i < revcount; i++)
2500 2499 seen[revs[i]] = 1ull << i;
2501 2500
2502 2501 interesting = revcount;
2503 2502
2504 2503 for (v = maxrev; v >= 0 && interesting; v--) {
2505 2504 bitmask sv = seen[v];
2506 2505 int parents[2];
2507 2506
2508 2507 if (!sv)
2509 2508 continue;
2510 2509
2511 2510 if (sv < poison) {
2512 2511 interesting -= 1;
2513 2512 if (sv == allseen) {
2514 2513 PyObject *obj = PyLong_FromLong(v);
2515 2514 if (obj == NULL)
2516 2515 goto bail;
2517 2516 if (PyList_Append(gca, obj) == -1) {
2518 2517 Py_DECREF(obj);
2519 2518 goto bail;
2520 2519 }
2521 2520 sv |= poison;
2522 2521 for (i = 0; i < revcount; i++) {
2523 2522 if (revs[i] == v)
2524 2523 goto done;
2525 2524 }
2526 2525 }
2527 2526 }
2528 2527 if (index_get_parents(self, v, parents, maxrev) < 0)
2529 2528 goto bail;
2530 2529
2531 2530 for (i = 0; i < 2; i++) {
2532 2531 int p = parents[i];
2533 2532 if (p == -1)
2534 2533 continue;
2535 2534 sp = seen[p];
2536 2535 if (sv < poison) {
2537 2536 if (sp == 0) {
2538 2537 seen[p] = sv;
2539 2538 interesting++;
2540 2539 } else if (sp != sv)
2541 2540 seen[p] |= sv;
2542 2541 } else {
2543 2542 if (sp && sp < poison)
2544 2543 interesting--;
2545 2544 seen[p] = sv;
2546 2545 }
2547 2546 }
2548 2547 }
2549 2548
2550 2549 done:
2551 2550 free(seen);
2552 2551 return gca;
2553 2552 bail:
2554 2553 free(seen);
2555 2554 Py_XDECREF(gca);
2556 2555 return NULL;
2557 2556 }
2558 2557
2559 2558 /*
2560 2559 * Given a disjoint set of revs, return the subset with the longest
2561 2560 * path to the root.
2562 2561 */
2563 2562 static PyObject *find_deepest(indexObject *self, PyObject *revs)
2564 2563 {
2565 2564 const Py_ssize_t revcount = PyList_GET_SIZE(revs);
2566 2565 static const Py_ssize_t capacity = 24;
2567 2566 int *depth, *interesting = NULL;
2568 2567 int i, j, v, ninteresting;
2569 2568 PyObject *dict = NULL, *keys = NULL;
2570 2569 long *seen = NULL;
2571 2570 int maxrev = -1;
2572 2571 long final;
2573 2572
2574 2573 if (revcount > capacity) {
2575 2574 PyErr_Format(PyExc_OverflowError,
2576 2575 "bitset size (%ld) > capacity (%ld)",
2577 2576 (long)revcount, (long)capacity);
2578 2577 return NULL;
2579 2578 }
2580 2579
2581 2580 for (i = 0; i < revcount; i++) {
2582 2581 int n = (int)PyLong_AsLong(PyList_GET_ITEM(revs, i));
2583 2582 if (n > maxrev)
2584 2583 maxrev = n;
2585 2584 }
2586 2585
2587 2586 depth = calloc(sizeof(*depth), maxrev + 1);
2588 2587 if (depth == NULL)
2589 2588 return PyErr_NoMemory();
2590 2589
2591 2590 seen = calloc(sizeof(*seen), maxrev + 1);
2592 2591 if (seen == NULL) {
2593 2592 PyErr_NoMemory();
2594 2593 goto bail;
2595 2594 }
2596 2595
2597 2596 interesting = calloc(sizeof(*interesting), ((size_t)1) << revcount);
2598 2597 if (interesting == NULL) {
2599 2598 PyErr_NoMemory();
2600 2599 goto bail;
2601 2600 }
2602 2601
2603 2602 if (PyList_Sort(revs) == -1)
2604 2603 goto bail;
2605 2604
2606 2605 for (i = 0; i < revcount; i++) {
2607 2606 int n = (int)PyLong_AsLong(PyList_GET_ITEM(revs, i));
2608 2607 long b = 1l << i;
2609 2608 depth[n] = 1;
2610 2609 seen[n] = b;
2611 2610 interesting[b] = 1;
2612 2611 }
2613 2612
2614 2613 /* invariant: ninteresting is the number of non-zero entries in
2615 2614 * interesting. */
2616 2615 ninteresting = (int)revcount;
2617 2616
2618 2617 for (v = maxrev; v >= 0 && ninteresting > 1; v--) {
2619 2618 int dv = depth[v];
2620 2619 int parents[2];
2621 2620 long sv;
2622 2621
2623 2622 if (dv == 0)
2624 2623 continue;
2625 2624
2626 2625 sv = seen[v];
2627 2626 if (index_get_parents(self, v, parents, maxrev) < 0)
2628 2627 goto bail;
2629 2628
2630 2629 for (i = 0; i < 2; i++) {
2631 2630 int p = parents[i];
2632 2631 long sp;
2633 2632 int dp;
2634 2633
2635 2634 if (p == -1)
2636 2635 continue;
2637 2636
2638 2637 dp = depth[p];
2639 2638 sp = seen[p];
2640 2639 if (dp <= dv) {
2641 2640 depth[p] = dv + 1;
2642 2641 if (sp != sv) {
2643 2642 interesting[sv] += 1;
2644 2643 seen[p] = sv;
2645 2644 if (sp) {
2646 2645 interesting[sp] -= 1;
2647 2646 if (interesting[sp] == 0)
2648 2647 ninteresting -= 1;
2649 2648 }
2650 2649 }
2651 2650 } else if (dv == dp - 1) {
2652 2651 long nsp = sp | sv;
2653 2652 if (nsp == sp)
2654 2653 continue;
2655 2654 seen[p] = nsp;
2656 2655 interesting[sp] -= 1;
2657 2656 if (interesting[sp] == 0)
2658 2657 ninteresting -= 1;
2659 2658 if (interesting[nsp] == 0)
2660 2659 ninteresting += 1;
2661 2660 interesting[nsp] += 1;
2662 2661 }
2663 2662 }
2664 2663 interesting[sv] -= 1;
2665 2664 if (interesting[sv] == 0)
2666 2665 ninteresting -= 1;
2667 2666 }
2668 2667
2669 2668 final = 0;
2670 2669 j = ninteresting;
2671 2670 for (i = 0; i < (int)(2 << revcount) && j > 0; i++) {
2672 2671 if (interesting[i] == 0)
2673 2672 continue;
2674 2673 final |= i;
2675 2674 j -= 1;
2676 2675 }
2677 2676 if (final == 0) {
2678 2677 keys = PyList_New(0);
2679 2678 goto bail;
2680 2679 }
2681 2680
2682 2681 dict = PyDict_New();
2683 2682 if (dict == NULL)
2684 2683 goto bail;
2685 2684
2686 2685 for (i = 0; i < revcount; i++) {
2687 2686 PyObject *key;
2688 2687
2689 2688 if ((final & (1 << i)) == 0)
2690 2689 continue;
2691 2690
2692 2691 key = PyList_GET_ITEM(revs, i);
2693 2692 Py_INCREF(key);
2694 2693 Py_INCREF(Py_None);
2695 2694 if (PyDict_SetItem(dict, key, Py_None) == -1) {
2696 2695 Py_DECREF(key);
2697 2696 Py_DECREF(Py_None);
2698 2697 goto bail;
2699 2698 }
2700 2699 }
2701 2700
2702 2701 keys = PyDict_Keys(dict);
2703 2702
2704 2703 bail:
2705 2704 free(depth);
2706 2705 free(seen);
2707 2706 free(interesting);
2708 2707 Py_XDECREF(dict);
2709 2708
2710 2709 return keys;
2711 2710 }
2712 2711
2713 2712 /*
2714 2713 * Given a (possibly overlapping) set of revs, return all the
2715 2714 * common ancestors heads: heads(::args[0] and ::a[1] and ...)
2716 2715 */
2717 2716 static PyObject *index_commonancestorsheads(indexObject *self, PyObject *args)
2718 2717 {
2719 2718 PyObject *ret = NULL;
2720 2719 Py_ssize_t argcount, i, len;
2721 2720 bitmask repeat = 0;
2722 2721 int revcount = 0;
2723 2722 int *revs;
2724 2723
2725 2724 argcount = PySequence_Length(args);
2726 2725 revs = PyMem_Malloc(argcount * sizeof(*revs));
2727 2726 if (argcount > 0 && revs == NULL)
2728 2727 return PyErr_NoMemory();
2729 2728 len = index_length(self);
2730 2729
2731 2730 for (i = 0; i < argcount; i++) {
2732 2731 static const int capacity = 24;
2733 2732 PyObject *obj = PySequence_GetItem(args, i);
2734 2733 bitmask x;
2735 2734 long val;
2736 2735
2737 2736 if (!PyLong_Check(obj)) {
2738 2737 PyErr_SetString(PyExc_TypeError,
2739 2738 "arguments must all be ints");
2740 2739 Py_DECREF(obj);
2741 2740 goto bail;
2742 2741 }
2743 2742 val = PyLong_AsLong(obj);
2744 2743 Py_DECREF(obj);
2745 2744 if (val == -1) {
2746 2745 ret = PyList_New(0);
2747 2746 goto done;
2748 2747 }
2749 2748 if (val < 0 || val >= len) {
2750 2749 PyErr_SetString(PyExc_IndexError, "index out of range");
2751 2750 goto bail;
2752 2751 }
2753 2752 /* this cheesy bloom filter lets us avoid some more
2754 2753 * expensive duplicate checks in the common set-is-disjoint
2755 2754 * case */
2756 2755 x = 1ull << (val & 0x3f);
2757 2756 if (repeat & x) {
2758 2757 int k;
2759 2758 for (k = 0; k < revcount; k++) {
2760 2759 if (val == revs[k])
2761 2760 goto duplicate;
2762 2761 }
2763 2762 } else
2764 2763 repeat |= x;
2765 2764 if (revcount >= capacity) {
2766 2765 PyErr_Format(PyExc_OverflowError,
2767 2766 "bitset size (%d) > capacity (%d)",
2768 2767 revcount, capacity);
2769 2768 goto bail;
2770 2769 }
2771 2770 revs[revcount++] = (int)val;
2772 2771 duplicate:;
2773 2772 }
2774 2773
2775 2774 if (revcount == 0) {
2776 2775 ret = PyList_New(0);
2777 2776 goto done;
2778 2777 }
2779 2778 if (revcount == 1) {
2780 2779 PyObject *obj;
2781 2780 ret = PyList_New(1);
2782 2781 if (ret == NULL)
2783 2782 goto bail;
2784 2783 obj = PyLong_FromLong(revs[0]);
2785 2784 if (obj == NULL)
2786 2785 goto bail;
2787 2786 PyList_SET_ITEM(ret, 0, obj);
2788 2787 goto done;
2789 2788 }
2790 2789
2791 2790 ret = find_gca_candidates(self, revs, revcount);
2792 2791 if (ret == NULL)
2793 2792 goto bail;
2794 2793
2795 2794 done:
2796 2795 PyMem_Free(revs);
2797 2796 return ret;
2798 2797
2799 2798 bail:
2800 2799 PyMem_Free(revs);
2801 2800 Py_XDECREF(ret);
2802 2801 return NULL;
2803 2802 }
2804 2803
2805 2804 /*
2806 2805 * Given a (possibly overlapping) set of revs, return the greatest
2807 2806 * common ancestors: those with the longest path to the root.
2808 2807 */
2809 2808 static PyObject *index_ancestors(indexObject *self, PyObject *args)
2810 2809 {
2811 2810 PyObject *ret;
2812 2811 PyObject *gca = index_commonancestorsheads(self, args);
2813 2812 if (gca == NULL)
2814 2813 return NULL;
2815 2814
2816 2815 if (PyList_GET_SIZE(gca) <= 1) {
2817 2816 return gca;
2818 2817 }
2819 2818
2820 2819 ret = find_deepest(self, gca);
2821 2820 Py_DECREF(gca);
2822 2821 return ret;
2823 2822 }
2824 2823
2825 2824 /*
2826 2825 * Invalidate any trie entries introduced by added revs.
2827 2826 */
2828 2827 static void index_invalidate_added(indexObject *self, Py_ssize_t start)
2829 2828 {
2830 2829 Py_ssize_t i, len;
2831 2830
2832 2831 len = self->length + self->new_length;
2833 2832 i = start - self->length;
2834 2833 if (i < 0)
2835 2834 return;
2836 2835
2837 2836 for (i = start; i < len; i++) {
2838 2837 const char *node = index_node(self, i);
2839 2838 nt_delete_node(&self->nt, node);
2840 2839 }
2841 2840
2842 2841 self->new_length = start - self->length;
2843 2842 }
2844 2843
2845 2844 /*
2846 2845 * Delete a numeric range of revs, which must be at the end of the
2847 2846 * range.
2848 2847 */
2849 2848 static int index_slice_del(indexObject *self, PyObject *item)
2850 2849 {
2851 2850 Py_ssize_t start, stop, step, slicelength;
2852 2851 Py_ssize_t length = index_length(self) + 1;
2853 2852 int ret = 0;
2854 2853
2855 2854 if (PySlice_GetIndicesEx(item, length, &start, &stop, &step,
2856 2855 &slicelength) < 0)
2857 2856 return -1;
2858 2857
2859 2858 if (slicelength <= 0)
2860 2859 return 0;
2861 2860
2862 2861 if ((step < 0 && start < stop) || (step > 0 && start > stop))
2863 2862 stop = start;
2864 2863
2865 2864 if (step < 0) {
2866 2865 stop = start + 1;
2867 2866 start = stop + step * (slicelength - 1) - 1;
2868 2867 step = -step;
2869 2868 }
2870 2869
2871 2870 if (step != 1) {
2872 2871 PyErr_SetString(PyExc_ValueError,
2873 2872 "revlog index delete requires step size of 1");
2874 2873 return -1;
2875 2874 }
2876 2875
2877 2876 if (stop != length - 1) {
2878 2877 PyErr_SetString(PyExc_IndexError,
2879 2878 "revlog index deletion indices are invalid");
2880 2879 return -1;
2881 2880 }
2882 2881
2883 2882 if (start < self->length) {
2884 2883 if (self->ntinitialized) {
2885 2884 Py_ssize_t i;
2886 2885
2887 2886 for (i = start; i < self->length; i++) {
2888 2887 const char *node = index_node_existing(self, i);
2889 2888 if (node == NULL)
2890 2889 return -1;
2891 2890
2892 2891 nt_delete_node(&self->nt, node);
2893 2892 }
2894 2893 if (self->new_length)
2895 2894 index_invalidate_added(self, self->length);
2896 2895 if (self->ntrev > start)
2897 2896 self->ntrev = (int)start;
2898 2897 } else if (self->new_length) {
2899 2898 self->new_length = 0;
2900 2899 }
2901 2900
2902 2901 self->length = start;
2903 2902 goto done;
2904 2903 }
2905 2904
2906 2905 if (self->ntinitialized) {
2907 2906 index_invalidate_added(self, start);
2908 2907 if (self->ntrev > start)
2909 2908 self->ntrev = (int)start;
2910 2909 } else {
2911 2910 self->new_length = start - self->length;
2912 2911 }
2913 2912 done:
2914 2913 Py_CLEAR(self->headrevs);
2915 2914 return ret;
2916 2915 }
2917 2916
2918 2917 /*
2919 2918 * Supported ops:
2920 2919 *
2921 2920 * slice deletion
2922 2921 * string assignment (extend node->rev mapping)
2923 2922 * string deletion (shrink node->rev mapping)
2924 2923 */
2925 2924 static int index_assign_subscript(indexObject *self, PyObject *item,
2926 2925 PyObject *value)
2927 2926 {
2928 2927 char *node;
2929 2928 long rev;
2930 2929
2931 2930 if (PySlice_Check(item) && value == NULL)
2932 2931 return index_slice_del(self, item);
2933 2932
2934 2933 if (node_check(self->nodelen, item, &node) == -1)
2935 2934 return -1;
2936 2935
2937 2936 if (value == NULL)
2938 2937 return self->ntinitialized ? nt_delete_node(&self->nt, node)
2939 2938 : 0;
2940 2939 rev = PyLong_AsLong(value);
2941 2940 if (rev > INT_MAX || rev < 0) {
2942 2941 if (!PyErr_Occurred())
2943 2942 PyErr_SetString(PyExc_ValueError, "rev out of range");
2944 2943 return -1;
2945 2944 }
2946 2945
2947 2946 if (index_init_nt(self) == -1)
2948 2947 return -1;
2949 2948 return nt_insert(&self->nt, node, (int)rev);
2950 2949 }
2951 2950
2952 2951 /*
2953 2952 * Find all RevlogNG entries in an index that has inline data. Update
2954 2953 * the optional "offsets" table with those entries.
2955 2954 */
2956 2955 static Py_ssize_t inline_scan(indexObject *self, const char **offsets)
2957 2956 {
2958 2957 const char *data = (const char *)self->buf.buf;
2959 2958 Py_ssize_t pos = 0;
2960 2959 Py_ssize_t end = self->buf.len;
2961 2960 long incr = self->entry_size;
2962 2961 Py_ssize_t len = 0;
2963 2962
2964 2963 while (pos + self->entry_size <= end && pos >= 0) {
2965 2964 uint32_t comp_len, sidedata_comp_len = 0;
2966 2965 /* 3rd element of header is length of compressed inline data */
2967 2966 if (self->format_version == format_v1) {
2968 2967 comp_len =
2969 2968 getbe32(data + pos + entry_v1_offset_comp_len);
2970 2969 sidedata_comp_len = 0;
2971 2970 } else if (self->format_version == format_v2) {
2972 2971 comp_len =
2973 2972 getbe32(data + pos + entry_v2_offset_comp_len);
2974 2973 sidedata_comp_len = getbe32(
2975 2974 data + pos + entry_v2_offset_sidedata_comp_len);
2976 2975 } else {
2977 2976 raise_revlog_error();
2978 2977 return -1;
2979 2978 }
2980 2979 incr = self->entry_size + comp_len + sidedata_comp_len;
2981 2980 if (offsets)
2982 2981 offsets[len] = data + pos;
2983 2982 len++;
2984 2983 pos += incr;
2985 2984 }
2986 2985
2987 2986 if (pos != end) {
2988 2987 if (!PyErr_Occurred())
2989 2988 PyErr_SetString(PyExc_ValueError, "corrupt index file");
2990 2989 return -1;
2991 2990 }
2992 2991
2993 2992 return len;
2994 2993 }
2995 2994
2996 2995 static int index_init(indexObject *self, PyObject *args, PyObject *kwargs)
2997 2996 {
2998 2997 PyObject *data_obj, *inlined_obj;
2999 2998 Py_ssize_t size;
3000 2999
3001 3000 static char *kwlist[] = {"data", "inlined", "format", NULL};
3002 3001
3003 3002 /* Initialize before argument-checking to avoid index_dealloc() crash.
3004 3003 */
3005 3004 self->added = NULL;
3006 3005 self->new_length = 0;
3007 3006 self->added_length = 0;
3008 3007 self->data = NULL;
3009 3008 memset(&self->buf, 0, sizeof(self->buf));
3010 3009 self->headrevs = NULL;
3011 3010 self->filteredrevs = Py_None;
3012 3011 Py_INCREF(Py_None);
3013 3012 self->ntinitialized = 0;
3014 3013 self->offsets = NULL;
3015 3014 self->nodelen = 20;
3016 3015 self->nullentry = NULL;
3017 3016 self->rust_ext_compat = 1;
3018 3017 self->format_version = format_v1;
3019 3018
3020 3019 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|l", kwlist,
3021 3020 &data_obj, &inlined_obj,
3022 3021 &(self->format_version)))
3023 3022 return -1;
3024 3023 if (!PyObject_CheckBuffer(data_obj)) {
3025 3024 PyErr_SetString(PyExc_TypeError,
3026 3025 "data does not support buffer interface");
3027 3026 return -1;
3028 3027 }
3029 3028 if (self->nodelen < 20 || self->nodelen > (Py_ssize_t)sizeof(nullid)) {
3030 3029 PyErr_SetString(PyExc_RuntimeError, "unsupported node size");
3031 3030 return -1;
3032 3031 }
3033 3032
3034 3033 if (self->format_version == format_v1) {
3035 3034 self->entry_size = v1_entry_size;
3036 3035 } else if (self->format_version == format_v2) {
3037 3036 self->entry_size = v2_entry_size;
3038 3037 } else if (self->format_version == format_cl2) {
3039 3038 self->entry_size = cl2_entry_size;
3040 3039 }
3041 3040
3042 3041 self->nullentry = Py_BuildValue(
3043 3042 "iiiiiiiy#iiBBi", 0, 0, 0, -1, -1, -1, -1, nullid, self->nodelen, 0,
3044 3043 0, comp_mode_inline, comp_mode_inline, rank_unknown);
3045 3044
3046 3045 if (!self->nullentry)
3047 3046 return -1;
3048 3047 PyObject_GC_UnTrack(self->nullentry);
3049 3048
3050 3049 if (PyObject_GetBuffer(data_obj, &self->buf, PyBUF_SIMPLE) == -1)
3051 3050 return -1;
3052 3051 size = self->buf.len;
3053 3052
3054 3053 self->inlined = inlined_obj && PyObject_IsTrue(inlined_obj);
3055 3054 self->data = data_obj;
3056 3055
3057 3056 self->ntlookups = self->ntmisses = 0;
3058 3057 self->ntrev = -1;
3059 3058 Py_INCREF(self->data);
3060 3059
3061 3060 if (self->inlined) {
3062 3061 Py_ssize_t len = inline_scan(self, NULL);
3063 3062 if (len == -1)
3064 3063 goto bail;
3065 3064 self->length = len;
3066 3065 } else {
3067 3066 if (size % self->entry_size) {
3068 3067 PyErr_SetString(PyExc_ValueError, "corrupt index file");
3069 3068 goto bail;
3070 3069 }
3071 3070 self->length = size / self->entry_size;
3072 3071 }
3073 3072
3074 3073 return 0;
3075 3074 bail:
3076 3075 return -1;
3077 3076 }
3078 3077
3079 3078 static PyObject *index_nodemap(indexObject *self)
3080 3079 {
3081 3080 Py_INCREF(self);
3082 3081 return (PyObject *)self;
3083 3082 }
3084 3083
3085 3084 static void _index_clearcaches(indexObject *self)
3086 3085 {
3087 3086 if (self->offsets) {
3088 3087 PyMem_Free((void *)self->offsets);
3089 3088 self->offsets = NULL;
3090 3089 }
3091 3090 if (self->ntinitialized) {
3092 3091 nt_dealloc(&self->nt);
3093 3092 }
3094 3093 self->ntinitialized = 0;
3095 3094 Py_CLEAR(self->headrevs);
3096 3095 }
3097 3096
3098 3097 static PyObject *index_clearcaches(indexObject *self)
3099 3098 {
3100 3099 _index_clearcaches(self);
3101 3100 self->ntrev = -1;
3102 3101 self->ntlookups = self->ntmisses = 0;
3103 3102 Py_RETURN_NONE;
3104 3103 }
3105 3104
3106 3105 static void index_dealloc(indexObject *self)
3107 3106 {
3108 3107 _index_clearcaches(self);
3109 3108 Py_XDECREF(self->filteredrevs);
3110 3109 if (self->buf.buf) {
3111 3110 PyBuffer_Release(&self->buf);
3112 3111 memset(&self->buf, 0, sizeof(self->buf));
3113 3112 }
3114 3113 Py_XDECREF(self->data);
3115 3114 PyMem_Free(self->added);
3116 3115 Py_XDECREF(self->nullentry);
3117 3116 PyObject_Del(self);
3118 3117 }
3119 3118
3120 3119 static PySequenceMethods index_sequence_methods = {
3121 3120 (lenfunc)index_length, /* sq_length */
3122 3121 0, /* sq_concat */
3123 3122 0, /* sq_repeat */
3124 3123 (ssizeargfunc)index_get, /* sq_item */
3125 3124 0, /* sq_slice */
3126 3125 0, /* sq_ass_item */
3127 3126 0, /* sq_ass_slice */
3128 3127 (objobjproc)index_contains, /* sq_contains */
3129 3128 };
3130 3129
3131 3130 static PyMappingMethods index_mapping_methods = {
3132 3131 (lenfunc)index_length, /* mp_length */
3133 3132 (binaryfunc)index_getitem, /* mp_subscript */
3134 3133 (objobjargproc)index_assign_subscript, /* mp_ass_subscript */
3135 3134 };
3136 3135
3137 3136 static PyMethodDef index_methods[] = {
3138 3137 {"ancestors", (PyCFunction)index_ancestors, METH_VARARGS,
3139 3138 "return the gca set of the given revs"},
3140 3139 {"commonancestorsheads", (PyCFunction)index_commonancestorsheads,
3141 3140 METH_VARARGS,
3142 3141 "return the heads of the common ancestors of the given revs"},
3143 3142 {"clearcaches", (PyCFunction)index_clearcaches, METH_NOARGS,
3144 3143 "clear the index caches"},
3145 3144 {"get", (PyCFunction)index_m_get, METH_VARARGS, "get an index entry"},
3146 3145 {"get_rev", (PyCFunction)index_m_get, METH_VARARGS,
3147 3146 "return `rev` associated with a node or None"},
3148 3147 {"has_node", (PyCFunction)index_m_has_node, METH_O,
3149 3148 "return True if the node exist in the index"},
3150 3149 {"rev", (PyCFunction)index_m_rev, METH_O,
3151 3150 "return `rev` associated with a node or raise RevlogError"},
3152 3151 {"computephasesmapsets", (PyCFunction)compute_phases_map_sets, METH_VARARGS,
3153 3152 "compute phases"},
3154 3153 {"reachableroots2", (PyCFunction)reachableroots2, METH_VARARGS,
3155 3154 "reachableroots"},
3156 3155 {"replace_sidedata_info", (PyCFunction)index_replace_sidedata_info,
3157 3156 METH_VARARGS, "replace an existing index entry with a new value"},
3158 3157 {"headrevs", (PyCFunction)index_headrevs, METH_VARARGS,
3159 3158 "get head revisions"}, /* Can do filtering since 3.2 */
3160 3159 {"headrevsfiltered", (PyCFunction)index_headrevs, METH_VARARGS,
3161 3160 "get filtered head revisions"}, /* Can always do filtering */
3162 3161 {"issnapshot", (PyCFunction)index_issnapshot, METH_O,
3163 3162 "True if the object is a snapshot"},
3164 3163 {"findsnapshots", (PyCFunction)index_findsnapshots, METH_VARARGS,
3165 3164 "Gather snapshot data in a cache dict"},
3166 3165 {"deltachain", (PyCFunction)index_deltachain, METH_VARARGS,
3167 3166 "determine revisions with deltas to reconstruct fulltext"},
3168 3167 {"slicechunktodensity", (PyCFunction)index_slicechunktodensity,
3169 3168 METH_VARARGS, "determine revisions with deltas to reconstruct fulltext"},
3170 3169 {"append", (PyCFunction)index_append, METH_O, "append an index entry"},
3171 3170 {"partialmatch", (PyCFunction)index_partialmatch, METH_VARARGS,
3172 3171 "match a potentially ambiguous node ID"},
3173 3172 {"shortest", (PyCFunction)index_shortest, METH_VARARGS,
3174 3173 "find length of shortest hex nodeid of a binary ID"},
3175 3174 {"stats", (PyCFunction)index_stats, METH_NOARGS, "stats for the index"},
3176 3175 {"entry_binary", (PyCFunction)index_entry_binary, METH_O,
3177 3176 "return an entry in binary form"},
3178 3177 {"pack_header", (PyCFunction)index_pack_header, METH_VARARGS,
3179 3178 "pack the revlog header information into binary"},
3180 3179 {NULL} /* Sentinel */
3181 3180 };
3182 3181
3183 3182 static PyGetSetDef index_getset[] = {
3184 3183 {"nodemap", (getter)index_nodemap, NULL, "nodemap", NULL},
3185 3184 {NULL} /* Sentinel */
3186 3185 };
3187 3186
3188 3187 static PyMemberDef index_members[] = {
3189 3188 {"entry_size", T_LONG, offsetof(indexObject, entry_size), 0,
3190 3189 "size of an index entry"},
3191 3190 {"rust_ext_compat", T_LONG, offsetof(indexObject, rust_ext_compat), 0,
3192 3191 "size of an index entry"},
3193 3192 {NULL} /* Sentinel */
3194 3193 };
3195 3194
3196 3195 PyTypeObject HgRevlogIndex_Type = {
3197 3196 PyVarObject_HEAD_INIT(NULL, 0) /* header */
3198 3197 "parsers.index", /* tp_name */
3199 3198 sizeof(indexObject), /* tp_basicsize */
3200 3199 0, /* tp_itemsize */
3201 3200 (destructor)index_dealloc, /* tp_dealloc */
3202 3201 0, /* tp_print */
3203 3202 0, /* tp_getattr */
3204 3203 0, /* tp_setattr */
3205 3204 0, /* tp_compare */
3206 3205 0, /* tp_repr */
3207 3206 0, /* tp_as_number */
3208 3207 &index_sequence_methods, /* tp_as_sequence */
3209 3208 &index_mapping_methods, /* tp_as_mapping */
3210 3209 0, /* tp_hash */
3211 3210 0, /* tp_call */
3212 3211 0, /* tp_str */
3213 3212 0, /* tp_getattro */
3214 3213 0, /* tp_setattro */
3215 3214 0, /* tp_as_buffer */
3216 3215 Py_TPFLAGS_DEFAULT, /* tp_flags */
3217 3216 "revlog index", /* tp_doc */
3218 3217 0, /* tp_traverse */
3219 3218 0, /* tp_clear */
3220 3219 0, /* tp_richcompare */
3221 3220 0, /* tp_weaklistoffset */
3222 3221 0, /* tp_iter */
3223 3222 0, /* tp_iternext */
3224 3223 index_methods, /* tp_methods */
3225 3224 index_members, /* tp_members */
3226 3225 index_getset, /* tp_getset */
3227 3226 0, /* tp_base */
3228 3227 0, /* tp_dict */
3229 3228 0, /* tp_descr_get */
3230 3229 0, /* tp_descr_set */
3231 3230 0, /* tp_dictoffset */
3232 3231 (initproc)index_init, /* tp_init */
3233 3232 0, /* tp_alloc */
3234 3233 };
3235 3234
3236 3235 /*
3237 3236 * returns a tuple of the form (index, cache) with elements as
3238 3237 * follows:
3239 3238 *
3240 3239 * index: an index object that lazily parses Revlog (v1 or v2) records
3241 3240 * cache: if data is inlined, a tuple (0, index_file_content), else None
3242 3241 * index_file_content could be a string, or a buffer
3243 3242 *
3244 3243 * added complications are for backwards compatibility
3245 3244 */
3246 3245 PyObject *parse_index2(PyObject *self, PyObject *args, PyObject *kwargs)
3247 3246 {
3248 3247 PyObject *cache = NULL;
3249 3248 indexObject *idx;
3250 3249 int ret;
3251 3250
3252 3251 idx = PyObject_New(indexObject, &HgRevlogIndex_Type);
3253 3252 if (idx == NULL)
3254 3253 goto bail;
3255 3254
3256 3255 ret = index_init(idx, args, kwargs);
3257 3256 if (ret == -1)
3258 3257 goto bail;
3259 3258
3260 3259 if (idx->inlined) {
3261 3260 cache = Py_BuildValue("iO", 0, idx->data);
3262 3261 if (cache == NULL)
3263 3262 goto bail;
3264 3263 } else {
3265 3264 cache = Py_None;
3266 3265 Py_INCREF(cache);
3267 3266 }
3268 3267
3269 3268 return Py_BuildValue("NN", idx, cache);
3270 3269
3271 3270 bail:
3272 3271 Py_XDECREF(idx);
3273 3272 Py_XDECREF(cache);
3274 3273 return NULL;
3275 3274 }
3276 3275
3277 3276 static Revlog_CAPI CAPI = {
3278 3277 /* increment the abi_version field upon each change in the Revlog_CAPI
3279 3278 struct or in the ABI of the listed functions */
3280 3279 3, index_length, index_node, index_fast_rank, HgRevlogIndex_GetParents,
3281 3280 };
3282 3281
3283 3282 void revlog_module_init(PyObject *mod)
3284 3283 {
3285 3284 PyObject *caps = NULL;
3286 3285 HgRevlogIndex_Type.tp_new = PyType_GenericNew;
3287 3286 if (PyType_Ready(&HgRevlogIndex_Type) < 0)
3288 3287 return;
3289 3288 Py_INCREF(&HgRevlogIndex_Type);
3290 3289 PyModule_AddObject(mod, "index", (PyObject *)&HgRevlogIndex_Type);
3291 3290
3292 3291 nodetreeType.tp_new = PyType_GenericNew;
3293 3292 if (PyType_Ready(&nodetreeType) < 0)
3294 3293 return;
3295 3294 Py_INCREF(&nodetreeType);
3296 3295 PyModule_AddObject(mod, "nodetree", (PyObject *)&nodetreeType);
3297 3296
3298 3297 caps = PyCapsule_New(&CAPI, "mercurial.cext.parsers.revlog_CAPI", NULL);
3299 3298 if (caps != NULL)
3300 3299 PyModule_AddObject(mod, "revlog_CAPI", caps);
3301 3300 }
General Comments 0
You need to be logged in to leave comments. Login now