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