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