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