##// END OF EJS Templates
index: add a `has_node` method (API)...
marmoute -
r43934:0c659fc2 default
parent child Browse files
Show More
@@ -1,762 +1,762 b''
1 /*
1 /*
2 parsers.c - efficient content parsing
2 parsers.c - efficient content parsing
3
3
4 Copyright 2008 Matt Mackall <mpm@selenic.com> and others
4 Copyright 2008 Matt Mackall <mpm@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 <ctype.h>
12 #include <ctype.h>
13 #include <stddef.h>
13 #include <stddef.h>
14 #include <string.h>
14 #include <string.h>
15
15
16 #include "bitmanipulation.h"
16 #include "bitmanipulation.h"
17 #include "charencode.h"
17 #include "charencode.h"
18 #include "util.h"
18 #include "util.h"
19
19
20 #ifdef IS_PY3K
20 #ifdef IS_PY3K
21 /* The mapping of Python types is meant to be temporary to get Python
21 /* The mapping of Python types is meant to be temporary to get Python
22 * 3 to compile. We should remove this once Python 3 support is fully
22 * 3 to compile. We should remove this once Python 3 support is fully
23 * supported and proper types are used in the extensions themselves. */
23 * supported and proper types are used in the extensions themselves. */
24 #define PyInt_Check PyLong_Check
24 #define PyInt_Check PyLong_Check
25 #define PyInt_FromLong PyLong_FromLong
25 #define PyInt_FromLong PyLong_FromLong
26 #define PyInt_FromSsize_t PyLong_FromSsize_t
26 #define PyInt_FromSsize_t PyLong_FromSsize_t
27 #define PyInt_AsLong PyLong_AsLong
27 #define PyInt_AsLong PyLong_AsLong
28 #endif
28 #endif
29
29
30 static const char *const versionerrortext = "Python minor version mismatch";
30 static const char *const versionerrortext = "Python minor version mismatch";
31
31
32 static PyObject *dict_new_presized(PyObject *self, PyObject *args)
32 static PyObject *dict_new_presized(PyObject *self, PyObject *args)
33 {
33 {
34 Py_ssize_t expected_size;
34 Py_ssize_t expected_size;
35
35
36 if (!PyArg_ParseTuple(args, "n:make_presized_dict", &expected_size)) {
36 if (!PyArg_ParseTuple(args, "n:make_presized_dict", &expected_size)) {
37 return NULL;
37 return NULL;
38 }
38 }
39
39
40 return _dict_new_presized(expected_size);
40 return _dict_new_presized(expected_size);
41 }
41 }
42
42
43 static inline dirstateTupleObject *make_dirstate_tuple(char state, int mode,
43 static inline dirstateTupleObject *make_dirstate_tuple(char state, int mode,
44 int size, int mtime)
44 int size, int mtime)
45 {
45 {
46 dirstateTupleObject *t =
46 dirstateTupleObject *t =
47 PyObject_New(dirstateTupleObject, &dirstateTupleType);
47 PyObject_New(dirstateTupleObject, &dirstateTupleType);
48 if (!t) {
48 if (!t) {
49 return NULL;
49 return NULL;
50 }
50 }
51 t->state = state;
51 t->state = state;
52 t->mode = mode;
52 t->mode = mode;
53 t->size = size;
53 t->size = size;
54 t->mtime = mtime;
54 t->mtime = mtime;
55 return t;
55 return t;
56 }
56 }
57
57
58 static PyObject *dirstate_tuple_new(PyTypeObject *subtype, PyObject *args,
58 static PyObject *dirstate_tuple_new(PyTypeObject *subtype, PyObject *args,
59 PyObject *kwds)
59 PyObject *kwds)
60 {
60 {
61 /* We do all the initialization here and not a tp_init function because
61 /* We do all the initialization here and not a tp_init function because
62 * dirstate_tuple is immutable. */
62 * dirstate_tuple is immutable. */
63 dirstateTupleObject *t;
63 dirstateTupleObject *t;
64 char state;
64 char state;
65 int size, mode, mtime;
65 int size, mode, mtime;
66 if (!PyArg_ParseTuple(args, "ciii", &state, &mode, &size, &mtime)) {
66 if (!PyArg_ParseTuple(args, "ciii", &state, &mode, &size, &mtime)) {
67 return NULL;
67 return NULL;
68 }
68 }
69
69
70 t = (dirstateTupleObject *)subtype->tp_alloc(subtype, 1);
70 t = (dirstateTupleObject *)subtype->tp_alloc(subtype, 1);
71 if (!t) {
71 if (!t) {
72 return NULL;
72 return NULL;
73 }
73 }
74 t->state = state;
74 t->state = state;
75 t->mode = mode;
75 t->mode = mode;
76 t->size = size;
76 t->size = size;
77 t->mtime = mtime;
77 t->mtime = mtime;
78
78
79 return (PyObject *)t;
79 return (PyObject *)t;
80 }
80 }
81
81
82 static void dirstate_tuple_dealloc(PyObject *o)
82 static void dirstate_tuple_dealloc(PyObject *o)
83 {
83 {
84 PyObject_Del(o);
84 PyObject_Del(o);
85 }
85 }
86
86
87 static Py_ssize_t dirstate_tuple_length(PyObject *o)
87 static Py_ssize_t dirstate_tuple_length(PyObject *o)
88 {
88 {
89 return 4;
89 return 4;
90 }
90 }
91
91
92 static PyObject *dirstate_tuple_item(PyObject *o, Py_ssize_t i)
92 static PyObject *dirstate_tuple_item(PyObject *o, Py_ssize_t i)
93 {
93 {
94 dirstateTupleObject *t = (dirstateTupleObject *)o;
94 dirstateTupleObject *t = (dirstateTupleObject *)o;
95 switch (i) {
95 switch (i) {
96 case 0:
96 case 0:
97 return PyBytes_FromStringAndSize(&t->state, 1);
97 return PyBytes_FromStringAndSize(&t->state, 1);
98 case 1:
98 case 1:
99 return PyInt_FromLong(t->mode);
99 return PyInt_FromLong(t->mode);
100 case 2:
100 case 2:
101 return PyInt_FromLong(t->size);
101 return PyInt_FromLong(t->size);
102 case 3:
102 case 3:
103 return PyInt_FromLong(t->mtime);
103 return PyInt_FromLong(t->mtime);
104 default:
104 default:
105 PyErr_SetString(PyExc_IndexError, "index out of range");
105 PyErr_SetString(PyExc_IndexError, "index out of range");
106 return NULL;
106 return NULL;
107 }
107 }
108 }
108 }
109
109
110 static PySequenceMethods dirstate_tuple_sq = {
110 static PySequenceMethods dirstate_tuple_sq = {
111 dirstate_tuple_length, /* sq_length */
111 dirstate_tuple_length, /* sq_length */
112 0, /* sq_concat */
112 0, /* sq_concat */
113 0, /* sq_repeat */
113 0, /* sq_repeat */
114 dirstate_tuple_item, /* sq_item */
114 dirstate_tuple_item, /* sq_item */
115 0, /* sq_ass_item */
115 0, /* sq_ass_item */
116 0, /* sq_contains */
116 0, /* sq_contains */
117 0, /* sq_inplace_concat */
117 0, /* sq_inplace_concat */
118 0 /* sq_inplace_repeat */
118 0 /* sq_inplace_repeat */
119 };
119 };
120
120
121 PyTypeObject dirstateTupleType = {
121 PyTypeObject dirstateTupleType = {
122 PyVarObject_HEAD_INIT(NULL, 0) /* header */
122 PyVarObject_HEAD_INIT(NULL, 0) /* header */
123 "dirstate_tuple", /* tp_name */
123 "dirstate_tuple", /* tp_name */
124 sizeof(dirstateTupleObject), /* tp_basicsize */
124 sizeof(dirstateTupleObject), /* tp_basicsize */
125 0, /* tp_itemsize */
125 0, /* tp_itemsize */
126 (destructor)dirstate_tuple_dealloc, /* tp_dealloc */
126 (destructor)dirstate_tuple_dealloc, /* tp_dealloc */
127 0, /* tp_print */
127 0, /* tp_print */
128 0, /* tp_getattr */
128 0, /* tp_getattr */
129 0, /* tp_setattr */
129 0, /* tp_setattr */
130 0, /* tp_compare */
130 0, /* tp_compare */
131 0, /* tp_repr */
131 0, /* tp_repr */
132 0, /* tp_as_number */
132 0, /* tp_as_number */
133 &dirstate_tuple_sq, /* tp_as_sequence */
133 &dirstate_tuple_sq, /* tp_as_sequence */
134 0, /* tp_as_mapping */
134 0, /* tp_as_mapping */
135 0, /* tp_hash */
135 0, /* tp_hash */
136 0, /* tp_call */
136 0, /* tp_call */
137 0, /* tp_str */
137 0, /* tp_str */
138 0, /* tp_getattro */
138 0, /* tp_getattro */
139 0, /* tp_setattro */
139 0, /* tp_setattro */
140 0, /* tp_as_buffer */
140 0, /* tp_as_buffer */
141 Py_TPFLAGS_DEFAULT, /* tp_flags */
141 Py_TPFLAGS_DEFAULT, /* tp_flags */
142 "dirstate tuple", /* tp_doc */
142 "dirstate tuple", /* tp_doc */
143 0, /* tp_traverse */
143 0, /* tp_traverse */
144 0, /* tp_clear */
144 0, /* tp_clear */
145 0, /* tp_richcompare */
145 0, /* tp_richcompare */
146 0, /* tp_weaklistoffset */
146 0, /* tp_weaklistoffset */
147 0, /* tp_iter */
147 0, /* tp_iter */
148 0, /* tp_iternext */
148 0, /* tp_iternext */
149 0, /* tp_methods */
149 0, /* tp_methods */
150 0, /* tp_members */
150 0, /* tp_members */
151 0, /* tp_getset */
151 0, /* tp_getset */
152 0, /* tp_base */
152 0, /* tp_base */
153 0, /* tp_dict */
153 0, /* tp_dict */
154 0, /* tp_descr_get */
154 0, /* tp_descr_get */
155 0, /* tp_descr_set */
155 0, /* tp_descr_set */
156 0, /* tp_dictoffset */
156 0, /* tp_dictoffset */
157 0, /* tp_init */
157 0, /* tp_init */
158 0, /* tp_alloc */
158 0, /* tp_alloc */
159 dirstate_tuple_new, /* tp_new */
159 dirstate_tuple_new, /* tp_new */
160 };
160 };
161
161
162 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
162 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
163 {
163 {
164 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
164 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
165 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
165 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
166 char state, *cur, *str, *cpos;
166 char state, *cur, *str, *cpos;
167 int mode, size, mtime;
167 int mode, size, mtime;
168 unsigned int flen, pos = 40;
168 unsigned int flen, pos = 40;
169 Py_ssize_t len = 40;
169 Py_ssize_t len = 40;
170 Py_ssize_t readlen;
170 Py_ssize_t readlen;
171
171
172 if (!PyArg_ParseTuple(
172 if (!PyArg_ParseTuple(
173 args, PY23("O!O!s#:parse_dirstate", "O!O!y#:parse_dirstate"),
173 args, PY23("O!O!s#:parse_dirstate", "O!O!y#:parse_dirstate"),
174 &PyDict_Type, &dmap, &PyDict_Type, &cmap, &str, &readlen)) {
174 &PyDict_Type, &dmap, &PyDict_Type, &cmap, &str, &readlen)) {
175 goto quit;
175 goto quit;
176 }
176 }
177
177
178 len = readlen;
178 len = readlen;
179
179
180 /* read parents */
180 /* read parents */
181 if (len < 40) {
181 if (len < 40) {
182 PyErr_SetString(PyExc_ValueError,
182 PyErr_SetString(PyExc_ValueError,
183 "too little data for parents");
183 "too little data for parents");
184 goto quit;
184 goto quit;
185 }
185 }
186
186
187 parents = Py_BuildValue(PY23("s#s#", "y#y#"), str, (Py_ssize_t)20,
187 parents = Py_BuildValue(PY23("s#s#", "y#y#"), str, (Py_ssize_t)20,
188 str + 20, (Py_ssize_t)20);
188 str + 20, (Py_ssize_t)20);
189 if (!parents) {
189 if (!parents) {
190 goto quit;
190 goto quit;
191 }
191 }
192
192
193 /* read filenames */
193 /* read filenames */
194 while (pos >= 40 && pos < len) {
194 while (pos >= 40 && pos < len) {
195 if (pos + 17 > len) {
195 if (pos + 17 > len) {
196 PyErr_SetString(PyExc_ValueError,
196 PyErr_SetString(PyExc_ValueError,
197 "overflow in dirstate");
197 "overflow in dirstate");
198 goto quit;
198 goto quit;
199 }
199 }
200 cur = str + pos;
200 cur = str + pos;
201 /* unpack header */
201 /* unpack header */
202 state = *cur;
202 state = *cur;
203 mode = getbe32(cur + 1);
203 mode = getbe32(cur + 1);
204 size = getbe32(cur + 5);
204 size = getbe32(cur + 5);
205 mtime = getbe32(cur + 9);
205 mtime = getbe32(cur + 9);
206 flen = getbe32(cur + 13);
206 flen = getbe32(cur + 13);
207 pos += 17;
207 pos += 17;
208 cur += 17;
208 cur += 17;
209 if (flen > len - pos) {
209 if (flen > len - pos) {
210 PyErr_SetString(PyExc_ValueError,
210 PyErr_SetString(PyExc_ValueError,
211 "overflow in dirstate");
211 "overflow in dirstate");
212 goto quit;
212 goto quit;
213 }
213 }
214
214
215 entry =
215 entry =
216 (PyObject *)make_dirstate_tuple(state, mode, size, mtime);
216 (PyObject *)make_dirstate_tuple(state, mode, size, mtime);
217 cpos = memchr(cur, 0, flen);
217 cpos = memchr(cur, 0, flen);
218 if (cpos) {
218 if (cpos) {
219 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
219 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
220 cname = PyBytes_FromStringAndSize(
220 cname = PyBytes_FromStringAndSize(
221 cpos + 1, flen - (cpos - cur) - 1);
221 cpos + 1, flen - (cpos - cur) - 1);
222 if (!fname || !cname ||
222 if (!fname || !cname ||
223 PyDict_SetItem(cmap, fname, cname) == -1 ||
223 PyDict_SetItem(cmap, fname, cname) == -1 ||
224 PyDict_SetItem(dmap, fname, entry) == -1) {
224 PyDict_SetItem(dmap, fname, entry) == -1) {
225 goto quit;
225 goto quit;
226 }
226 }
227 Py_DECREF(cname);
227 Py_DECREF(cname);
228 } else {
228 } else {
229 fname = PyBytes_FromStringAndSize(cur, flen);
229 fname = PyBytes_FromStringAndSize(cur, flen);
230 if (!fname ||
230 if (!fname ||
231 PyDict_SetItem(dmap, fname, entry) == -1) {
231 PyDict_SetItem(dmap, fname, entry) == -1) {
232 goto quit;
232 goto quit;
233 }
233 }
234 }
234 }
235 Py_DECREF(fname);
235 Py_DECREF(fname);
236 Py_DECREF(entry);
236 Py_DECREF(entry);
237 fname = cname = entry = NULL;
237 fname = cname = entry = NULL;
238 pos += flen;
238 pos += flen;
239 }
239 }
240
240
241 ret = parents;
241 ret = parents;
242 Py_INCREF(ret);
242 Py_INCREF(ret);
243 quit:
243 quit:
244 Py_XDECREF(fname);
244 Py_XDECREF(fname);
245 Py_XDECREF(cname);
245 Py_XDECREF(cname);
246 Py_XDECREF(entry);
246 Py_XDECREF(entry);
247 Py_XDECREF(parents);
247 Py_XDECREF(parents);
248 return ret;
248 return ret;
249 }
249 }
250
250
251 /*
251 /*
252 * Build a set of non-normal and other parent entries from the dirstate dmap
252 * Build a set of non-normal and other parent entries from the dirstate dmap
253 */
253 */
254 static PyObject *nonnormalotherparententries(PyObject *self, PyObject *args)
254 static PyObject *nonnormalotherparententries(PyObject *self, PyObject *args)
255 {
255 {
256 PyObject *dmap, *fname, *v;
256 PyObject *dmap, *fname, *v;
257 PyObject *nonnset = NULL, *otherpset = NULL, *result = NULL;
257 PyObject *nonnset = NULL, *otherpset = NULL, *result = NULL;
258 Py_ssize_t pos;
258 Py_ssize_t pos;
259
259
260 if (!PyArg_ParseTuple(args, "O!:nonnormalentries", &PyDict_Type,
260 if (!PyArg_ParseTuple(args, "O!:nonnormalentries", &PyDict_Type,
261 &dmap)) {
261 &dmap)) {
262 goto bail;
262 goto bail;
263 }
263 }
264
264
265 nonnset = PySet_New(NULL);
265 nonnset = PySet_New(NULL);
266 if (nonnset == NULL) {
266 if (nonnset == NULL) {
267 goto bail;
267 goto bail;
268 }
268 }
269
269
270 otherpset = PySet_New(NULL);
270 otherpset = PySet_New(NULL);
271 if (otherpset == NULL) {
271 if (otherpset == NULL) {
272 goto bail;
272 goto bail;
273 }
273 }
274
274
275 pos = 0;
275 pos = 0;
276 while (PyDict_Next(dmap, &pos, &fname, &v)) {
276 while (PyDict_Next(dmap, &pos, &fname, &v)) {
277 dirstateTupleObject *t;
277 dirstateTupleObject *t;
278 if (!dirstate_tuple_check(v)) {
278 if (!dirstate_tuple_check(v)) {
279 PyErr_SetString(PyExc_TypeError,
279 PyErr_SetString(PyExc_TypeError,
280 "expected a dirstate tuple");
280 "expected a dirstate tuple");
281 goto bail;
281 goto bail;
282 }
282 }
283 t = (dirstateTupleObject *)v;
283 t = (dirstateTupleObject *)v;
284
284
285 if (t->state == 'n' && t->size == -2) {
285 if (t->state == 'n' && t->size == -2) {
286 if (PySet_Add(otherpset, fname) == -1) {
286 if (PySet_Add(otherpset, fname) == -1) {
287 goto bail;
287 goto bail;
288 }
288 }
289 }
289 }
290
290
291 if (t->state == 'n' && t->mtime != -1) {
291 if (t->state == 'n' && t->mtime != -1) {
292 continue;
292 continue;
293 }
293 }
294 if (PySet_Add(nonnset, fname) == -1) {
294 if (PySet_Add(nonnset, fname) == -1) {
295 goto bail;
295 goto bail;
296 }
296 }
297 }
297 }
298
298
299 result = Py_BuildValue("(OO)", nonnset, otherpset);
299 result = Py_BuildValue("(OO)", nonnset, otherpset);
300 if (result == NULL) {
300 if (result == NULL) {
301 goto bail;
301 goto bail;
302 }
302 }
303 Py_DECREF(nonnset);
303 Py_DECREF(nonnset);
304 Py_DECREF(otherpset);
304 Py_DECREF(otherpset);
305 return result;
305 return result;
306 bail:
306 bail:
307 Py_XDECREF(nonnset);
307 Py_XDECREF(nonnset);
308 Py_XDECREF(otherpset);
308 Py_XDECREF(otherpset);
309 Py_XDECREF(result);
309 Py_XDECREF(result);
310 return NULL;
310 return NULL;
311 }
311 }
312
312
313 /*
313 /*
314 * Efficiently pack a dirstate object into its on-disk format.
314 * Efficiently pack a dirstate object into its on-disk format.
315 */
315 */
316 static PyObject *pack_dirstate(PyObject *self, PyObject *args)
316 static PyObject *pack_dirstate(PyObject *self, PyObject *args)
317 {
317 {
318 PyObject *packobj = NULL;
318 PyObject *packobj = NULL;
319 PyObject *map, *copymap, *pl, *mtime_unset = NULL;
319 PyObject *map, *copymap, *pl, *mtime_unset = NULL;
320 Py_ssize_t nbytes, pos, l;
320 Py_ssize_t nbytes, pos, l;
321 PyObject *k, *v = NULL, *pn;
321 PyObject *k, *v = NULL, *pn;
322 char *p, *s;
322 char *p, *s;
323 int now;
323 int now;
324
324
325 if (!PyArg_ParseTuple(args, "O!O!O!i:pack_dirstate", &PyDict_Type, &map,
325 if (!PyArg_ParseTuple(args, "O!O!O!i:pack_dirstate", &PyDict_Type, &map,
326 &PyDict_Type, &copymap, &PyTuple_Type, &pl,
326 &PyDict_Type, &copymap, &PyTuple_Type, &pl,
327 &now)) {
327 &now)) {
328 return NULL;
328 return NULL;
329 }
329 }
330
330
331 if (PyTuple_Size(pl) != 2) {
331 if (PyTuple_Size(pl) != 2) {
332 PyErr_SetString(PyExc_TypeError, "expected 2-element tuple");
332 PyErr_SetString(PyExc_TypeError, "expected 2-element tuple");
333 return NULL;
333 return NULL;
334 }
334 }
335
335
336 /* Figure out how much we need to allocate. */
336 /* Figure out how much we need to allocate. */
337 for (nbytes = 40, pos = 0; PyDict_Next(map, &pos, &k, &v);) {
337 for (nbytes = 40, pos = 0; PyDict_Next(map, &pos, &k, &v);) {
338 PyObject *c;
338 PyObject *c;
339 if (!PyBytes_Check(k)) {
339 if (!PyBytes_Check(k)) {
340 PyErr_SetString(PyExc_TypeError, "expected string key");
340 PyErr_SetString(PyExc_TypeError, "expected string key");
341 goto bail;
341 goto bail;
342 }
342 }
343 nbytes += PyBytes_GET_SIZE(k) + 17;
343 nbytes += PyBytes_GET_SIZE(k) + 17;
344 c = PyDict_GetItem(copymap, k);
344 c = PyDict_GetItem(copymap, k);
345 if (c) {
345 if (c) {
346 if (!PyBytes_Check(c)) {
346 if (!PyBytes_Check(c)) {
347 PyErr_SetString(PyExc_TypeError,
347 PyErr_SetString(PyExc_TypeError,
348 "expected string key");
348 "expected string key");
349 goto bail;
349 goto bail;
350 }
350 }
351 nbytes += PyBytes_GET_SIZE(c) + 1;
351 nbytes += PyBytes_GET_SIZE(c) + 1;
352 }
352 }
353 }
353 }
354
354
355 packobj = PyBytes_FromStringAndSize(NULL, nbytes);
355 packobj = PyBytes_FromStringAndSize(NULL, nbytes);
356 if (packobj == NULL) {
356 if (packobj == NULL) {
357 goto bail;
357 goto bail;
358 }
358 }
359
359
360 p = PyBytes_AS_STRING(packobj);
360 p = PyBytes_AS_STRING(packobj);
361
361
362 pn = PyTuple_GET_ITEM(pl, 0);
362 pn = PyTuple_GET_ITEM(pl, 0);
363 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
363 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
364 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
364 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
365 goto bail;
365 goto bail;
366 }
366 }
367 memcpy(p, s, l);
367 memcpy(p, s, l);
368 p += 20;
368 p += 20;
369 pn = PyTuple_GET_ITEM(pl, 1);
369 pn = PyTuple_GET_ITEM(pl, 1);
370 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
370 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
371 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
371 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
372 goto bail;
372 goto bail;
373 }
373 }
374 memcpy(p, s, l);
374 memcpy(p, s, l);
375 p += 20;
375 p += 20;
376
376
377 for (pos = 0; PyDict_Next(map, &pos, &k, &v);) {
377 for (pos = 0; PyDict_Next(map, &pos, &k, &v);) {
378 dirstateTupleObject *tuple;
378 dirstateTupleObject *tuple;
379 char state;
379 char state;
380 int mode, size, mtime;
380 int mode, size, mtime;
381 Py_ssize_t len, l;
381 Py_ssize_t len, l;
382 PyObject *o;
382 PyObject *o;
383 char *t;
383 char *t;
384
384
385 if (!dirstate_tuple_check(v)) {
385 if (!dirstate_tuple_check(v)) {
386 PyErr_SetString(PyExc_TypeError,
386 PyErr_SetString(PyExc_TypeError,
387 "expected a dirstate tuple");
387 "expected a dirstate tuple");
388 goto bail;
388 goto bail;
389 }
389 }
390 tuple = (dirstateTupleObject *)v;
390 tuple = (dirstateTupleObject *)v;
391
391
392 state = tuple->state;
392 state = tuple->state;
393 mode = tuple->mode;
393 mode = tuple->mode;
394 size = tuple->size;
394 size = tuple->size;
395 mtime = tuple->mtime;
395 mtime = tuple->mtime;
396 if (state == 'n' && mtime == now) {
396 if (state == 'n' && mtime == now) {
397 /* See pure/parsers.py:pack_dirstate for why we do
397 /* See pure/parsers.py:pack_dirstate for why we do
398 * this. */
398 * this. */
399 mtime = -1;
399 mtime = -1;
400 mtime_unset = (PyObject *)make_dirstate_tuple(
400 mtime_unset = (PyObject *)make_dirstate_tuple(
401 state, mode, size, mtime);
401 state, mode, size, mtime);
402 if (!mtime_unset) {
402 if (!mtime_unset) {
403 goto bail;
403 goto bail;
404 }
404 }
405 if (PyDict_SetItem(map, k, mtime_unset) == -1) {
405 if (PyDict_SetItem(map, k, mtime_unset) == -1) {
406 goto bail;
406 goto bail;
407 }
407 }
408 Py_DECREF(mtime_unset);
408 Py_DECREF(mtime_unset);
409 mtime_unset = NULL;
409 mtime_unset = NULL;
410 }
410 }
411 *p++ = state;
411 *p++ = state;
412 putbe32((uint32_t)mode, p);
412 putbe32((uint32_t)mode, p);
413 putbe32((uint32_t)size, p + 4);
413 putbe32((uint32_t)size, p + 4);
414 putbe32((uint32_t)mtime, p + 8);
414 putbe32((uint32_t)mtime, p + 8);
415 t = p + 12;
415 t = p + 12;
416 p += 16;
416 p += 16;
417 len = PyBytes_GET_SIZE(k);
417 len = PyBytes_GET_SIZE(k);
418 memcpy(p, PyBytes_AS_STRING(k), len);
418 memcpy(p, PyBytes_AS_STRING(k), len);
419 p += len;
419 p += len;
420 o = PyDict_GetItem(copymap, k);
420 o = PyDict_GetItem(copymap, k);
421 if (o) {
421 if (o) {
422 *p++ = '\0';
422 *p++ = '\0';
423 l = PyBytes_GET_SIZE(o);
423 l = PyBytes_GET_SIZE(o);
424 memcpy(p, PyBytes_AS_STRING(o), l);
424 memcpy(p, PyBytes_AS_STRING(o), l);
425 p += l;
425 p += l;
426 len += l + 1;
426 len += l + 1;
427 }
427 }
428 putbe32((uint32_t)len, t);
428 putbe32((uint32_t)len, t);
429 }
429 }
430
430
431 pos = p - PyBytes_AS_STRING(packobj);
431 pos = p - PyBytes_AS_STRING(packobj);
432 if (pos != nbytes) {
432 if (pos != nbytes) {
433 PyErr_Format(PyExc_SystemError, "bad dirstate size: %ld != %ld",
433 PyErr_Format(PyExc_SystemError, "bad dirstate size: %ld != %ld",
434 (long)pos, (long)nbytes);
434 (long)pos, (long)nbytes);
435 goto bail;
435 goto bail;
436 }
436 }
437
437
438 return packobj;
438 return packobj;
439 bail:
439 bail:
440 Py_XDECREF(mtime_unset);
440 Py_XDECREF(mtime_unset);
441 Py_XDECREF(packobj);
441 Py_XDECREF(packobj);
442 Py_XDECREF(v);
442 Py_XDECREF(v);
443 return NULL;
443 return NULL;
444 }
444 }
445
445
446 #define BUMPED_FIX 1
446 #define BUMPED_FIX 1
447 #define USING_SHA_256 2
447 #define USING_SHA_256 2
448 #define FM1_HEADER_SIZE (4 + 8 + 2 + 2 + 1 + 1 + 1)
448 #define FM1_HEADER_SIZE (4 + 8 + 2 + 2 + 1 + 1 + 1)
449
449
450 static PyObject *readshas(const char *source, unsigned char num,
450 static PyObject *readshas(const char *source, unsigned char num,
451 Py_ssize_t hashwidth)
451 Py_ssize_t hashwidth)
452 {
452 {
453 int i;
453 int i;
454 PyObject *list = PyTuple_New(num);
454 PyObject *list = PyTuple_New(num);
455 if (list == NULL) {
455 if (list == NULL) {
456 return NULL;
456 return NULL;
457 }
457 }
458 for (i = 0; i < num; i++) {
458 for (i = 0; i < num; i++) {
459 PyObject *hash = PyBytes_FromStringAndSize(source, hashwidth);
459 PyObject *hash = PyBytes_FromStringAndSize(source, hashwidth);
460 if (hash == NULL) {
460 if (hash == NULL) {
461 Py_DECREF(list);
461 Py_DECREF(list);
462 return NULL;
462 return NULL;
463 }
463 }
464 PyTuple_SET_ITEM(list, i, hash);
464 PyTuple_SET_ITEM(list, i, hash);
465 source += hashwidth;
465 source += hashwidth;
466 }
466 }
467 return list;
467 return list;
468 }
468 }
469
469
470 static PyObject *fm1readmarker(const char *databegin, const char *dataend,
470 static PyObject *fm1readmarker(const char *databegin, const char *dataend,
471 uint32_t *msize)
471 uint32_t *msize)
472 {
472 {
473 const char *data = databegin;
473 const char *data = databegin;
474 const char *meta;
474 const char *meta;
475
475
476 double mtime;
476 double mtime;
477 int16_t tz;
477 int16_t tz;
478 uint16_t flags;
478 uint16_t flags;
479 unsigned char nsuccs, nparents, nmetadata;
479 unsigned char nsuccs, nparents, nmetadata;
480 Py_ssize_t hashwidth = 20;
480 Py_ssize_t hashwidth = 20;
481
481
482 PyObject *prec = NULL, *parents = NULL, *succs = NULL;
482 PyObject *prec = NULL, *parents = NULL, *succs = NULL;
483 PyObject *metadata = NULL, *ret = NULL;
483 PyObject *metadata = NULL, *ret = NULL;
484 int i;
484 int i;
485
485
486 if (data + FM1_HEADER_SIZE > dataend) {
486 if (data + FM1_HEADER_SIZE > dataend) {
487 goto overflow;
487 goto overflow;
488 }
488 }
489
489
490 *msize = getbe32(data);
490 *msize = getbe32(data);
491 data += 4;
491 data += 4;
492 mtime = getbefloat64(data);
492 mtime = getbefloat64(data);
493 data += 8;
493 data += 8;
494 tz = getbeint16(data);
494 tz = getbeint16(data);
495 data += 2;
495 data += 2;
496 flags = getbeuint16(data);
496 flags = getbeuint16(data);
497 data += 2;
497 data += 2;
498
498
499 if (flags & USING_SHA_256) {
499 if (flags & USING_SHA_256) {
500 hashwidth = 32;
500 hashwidth = 32;
501 }
501 }
502
502
503 nsuccs = (unsigned char)(*data++);
503 nsuccs = (unsigned char)(*data++);
504 nparents = (unsigned char)(*data++);
504 nparents = (unsigned char)(*data++);
505 nmetadata = (unsigned char)(*data++);
505 nmetadata = (unsigned char)(*data++);
506
506
507 if (databegin + *msize > dataend) {
507 if (databegin + *msize > dataend) {
508 goto overflow;
508 goto overflow;
509 }
509 }
510 dataend = databegin + *msize; /* narrow down to marker size */
510 dataend = databegin + *msize; /* narrow down to marker size */
511
511
512 if (data + hashwidth > dataend) {
512 if (data + hashwidth > dataend) {
513 goto overflow;
513 goto overflow;
514 }
514 }
515 prec = PyBytes_FromStringAndSize(data, hashwidth);
515 prec = PyBytes_FromStringAndSize(data, hashwidth);
516 data += hashwidth;
516 data += hashwidth;
517 if (prec == NULL) {
517 if (prec == NULL) {
518 goto bail;
518 goto bail;
519 }
519 }
520
520
521 if (data + nsuccs * hashwidth > dataend) {
521 if (data + nsuccs * hashwidth > dataend) {
522 goto overflow;
522 goto overflow;
523 }
523 }
524 succs = readshas(data, nsuccs, hashwidth);
524 succs = readshas(data, nsuccs, hashwidth);
525 if (succs == NULL) {
525 if (succs == NULL) {
526 goto bail;
526 goto bail;
527 }
527 }
528 data += nsuccs * hashwidth;
528 data += nsuccs * hashwidth;
529
529
530 if (nparents == 1 || nparents == 2) {
530 if (nparents == 1 || nparents == 2) {
531 if (data + nparents * hashwidth > dataend) {
531 if (data + nparents * hashwidth > dataend) {
532 goto overflow;
532 goto overflow;
533 }
533 }
534 parents = readshas(data, nparents, hashwidth);
534 parents = readshas(data, nparents, hashwidth);
535 if (parents == NULL) {
535 if (parents == NULL) {
536 goto bail;
536 goto bail;
537 }
537 }
538 data += nparents * hashwidth;
538 data += nparents * hashwidth;
539 } else {
539 } else {
540 parents = Py_None;
540 parents = Py_None;
541 Py_INCREF(parents);
541 Py_INCREF(parents);
542 }
542 }
543
543
544 if (data + 2 * nmetadata > dataend) {
544 if (data + 2 * nmetadata > dataend) {
545 goto overflow;
545 goto overflow;
546 }
546 }
547 meta = data + (2 * nmetadata);
547 meta = data + (2 * nmetadata);
548 metadata = PyTuple_New(nmetadata);
548 metadata = PyTuple_New(nmetadata);
549 if (metadata == NULL) {
549 if (metadata == NULL) {
550 goto bail;
550 goto bail;
551 }
551 }
552 for (i = 0; i < nmetadata; i++) {
552 for (i = 0; i < nmetadata; i++) {
553 PyObject *tmp, *left = NULL, *right = NULL;
553 PyObject *tmp, *left = NULL, *right = NULL;
554 Py_ssize_t leftsize = (unsigned char)(*data++);
554 Py_ssize_t leftsize = (unsigned char)(*data++);
555 Py_ssize_t rightsize = (unsigned char)(*data++);
555 Py_ssize_t rightsize = (unsigned char)(*data++);
556 if (meta + leftsize + rightsize > dataend) {
556 if (meta + leftsize + rightsize > dataend) {
557 goto overflow;
557 goto overflow;
558 }
558 }
559 left = PyBytes_FromStringAndSize(meta, leftsize);
559 left = PyBytes_FromStringAndSize(meta, leftsize);
560 meta += leftsize;
560 meta += leftsize;
561 right = PyBytes_FromStringAndSize(meta, rightsize);
561 right = PyBytes_FromStringAndSize(meta, rightsize);
562 meta += rightsize;
562 meta += rightsize;
563 tmp = PyTuple_New(2);
563 tmp = PyTuple_New(2);
564 if (!left || !right || !tmp) {
564 if (!left || !right || !tmp) {
565 Py_XDECREF(left);
565 Py_XDECREF(left);
566 Py_XDECREF(right);
566 Py_XDECREF(right);
567 Py_XDECREF(tmp);
567 Py_XDECREF(tmp);
568 goto bail;
568 goto bail;
569 }
569 }
570 PyTuple_SET_ITEM(tmp, 0, left);
570 PyTuple_SET_ITEM(tmp, 0, left);
571 PyTuple_SET_ITEM(tmp, 1, right);
571 PyTuple_SET_ITEM(tmp, 1, right);
572 PyTuple_SET_ITEM(metadata, i, tmp);
572 PyTuple_SET_ITEM(metadata, i, tmp);
573 }
573 }
574 ret = Py_BuildValue("(OOHO(di)O)", prec, succs, flags, metadata, mtime,
574 ret = Py_BuildValue("(OOHO(di)O)", prec, succs, flags, metadata, mtime,
575 (int)tz * 60, parents);
575 (int)tz * 60, parents);
576 goto bail; /* return successfully */
576 goto bail; /* return successfully */
577
577
578 overflow:
578 overflow:
579 PyErr_SetString(PyExc_ValueError, "overflow in obsstore");
579 PyErr_SetString(PyExc_ValueError, "overflow in obsstore");
580 bail:
580 bail:
581 Py_XDECREF(prec);
581 Py_XDECREF(prec);
582 Py_XDECREF(succs);
582 Py_XDECREF(succs);
583 Py_XDECREF(metadata);
583 Py_XDECREF(metadata);
584 Py_XDECREF(parents);
584 Py_XDECREF(parents);
585 return ret;
585 return ret;
586 }
586 }
587
587
588 static PyObject *fm1readmarkers(PyObject *self, PyObject *args)
588 static PyObject *fm1readmarkers(PyObject *self, PyObject *args)
589 {
589 {
590 const char *data, *dataend;
590 const char *data, *dataend;
591 Py_ssize_t datalen, offset, stop;
591 Py_ssize_t datalen, offset, stop;
592 PyObject *markers = NULL;
592 PyObject *markers = NULL;
593
593
594 if (!PyArg_ParseTuple(args, PY23("s#nn", "y#nn"), &data, &datalen,
594 if (!PyArg_ParseTuple(args, PY23("s#nn", "y#nn"), &data, &datalen,
595 &offset, &stop)) {
595 &offset, &stop)) {
596 return NULL;
596 return NULL;
597 }
597 }
598 if (offset < 0) {
598 if (offset < 0) {
599 PyErr_SetString(PyExc_ValueError,
599 PyErr_SetString(PyExc_ValueError,
600 "invalid negative offset in fm1readmarkers");
600 "invalid negative offset in fm1readmarkers");
601 return NULL;
601 return NULL;
602 }
602 }
603 if (stop > datalen) {
603 if (stop > datalen) {
604 PyErr_SetString(
604 PyErr_SetString(
605 PyExc_ValueError,
605 PyExc_ValueError,
606 "stop longer than data length in fm1readmarkers");
606 "stop longer than data length in fm1readmarkers");
607 return NULL;
607 return NULL;
608 }
608 }
609 dataend = data + datalen;
609 dataend = data + datalen;
610 data += offset;
610 data += offset;
611 markers = PyList_New(0);
611 markers = PyList_New(0);
612 if (!markers) {
612 if (!markers) {
613 return NULL;
613 return NULL;
614 }
614 }
615 while (offset < stop) {
615 while (offset < stop) {
616 uint32_t msize;
616 uint32_t msize;
617 int error;
617 int error;
618 PyObject *record = fm1readmarker(data, dataend, &msize);
618 PyObject *record = fm1readmarker(data, dataend, &msize);
619 if (!record) {
619 if (!record) {
620 goto bail;
620 goto bail;
621 }
621 }
622 error = PyList_Append(markers, record);
622 error = PyList_Append(markers, record);
623 Py_DECREF(record);
623 Py_DECREF(record);
624 if (error) {
624 if (error) {
625 goto bail;
625 goto bail;
626 }
626 }
627 data += msize;
627 data += msize;
628 offset += msize;
628 offset += msize;
629 }
629 }
630 return markers;
630 return markers;
631 bail:
631 bail:
632 Py_DECREF(markers);
632 Py_DECREF(markers);
633 return NULL;
633 return NULL;
634 }
634 }
635
635
636 static char parsers_doc[] = "Efficient content parsing.";
636 static char parsers_doc[] = "Efficient content parsing.";
637
637
638 PyObject *encodedir(PyObject *self, PyObject *args);
638 PyObject *encodedir(PyObject *self, PyObject *args);
639 PyObject *pathencode(PyObject *self, PyObject *args);
639 PyObject *pathencode(PyObject *self, PyObject *args);
640 PyObject *lowerencode(PyObject *self, PyObject *args);
640 PyObject *lowerencode(PyObject *self, PyObject *args);
641 PyObject *parse_index2(PyObject *self, PyObject *args);
641 PyObject *parse_index2(PyObject *self, PyObject *args);
642
642
643 static PyMethodDef methods[] = {
643 static PyMethodDef methods[] = {
644 {"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"},
644 {"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"},
645 {"nonnormalotherparententries", nonnormalotherparententries, METH_VARARGS,
645 {"nonnormalotherparententries", nonnormalotherparententries, METH_VARARGS,
646 "create a set containing non-normal and other parent entries of given "
646 "create a set containing non-normal and other parent entries of given "
647 "dirstate\n"},
647 "dirstate\n"},
648 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
648 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
649 {"parse_index2", parse_index2, METH_VARARGS, "parse a revlog index\n"},
649 {"parse_index2", parse_index2, METH_VARARGS, "parse a revlog index\n"},
650 {"isasciistr", isasciistr, METH_VARARGS, "check if an ASCII string\n"},
650 {"isasciistr", isasciistr, METH_VARARGS, "check if an ASCII string\n"},
651 {"asciilower", asciilower, METH_VARARGS, "lowercase an ASCII string\n"},
651 {"asciilower", asciilower, METH_VARARGS, "lowercase an ASCII string\n"},
652 {"asciiupper", asciiupper, METH_VARARGS, "uppercase an ASCII string\n"},
652 {"asciiupper", asciiupper, METH_VARARGS, "uppercase an ASCII string\n"},
653 {"dict_new_presized", dict_new_presized, METH_VARARGS,
653 {"dict_new_presized", dict_new_presized, METH_VARARGS,
654 "construct a dict with an expected size\n"},
654 "construct a dict with an expected size\n"},
655 {"make_file_foldmap", make_file_foldmap, METH_VARARGS,
655 {"make_file_foldmap", make_file_foldmap, METH_VARARGS,
656 "make file foldmap\n"},
656 "make file foldmap\n"},
657 {"jsonescapeu8fast", jsonescapeu8fast, METH_VARARGS,
657 {"jsonescapeu8fast", jsonescapeu8fast, METH_VARARGS,
658 "escape a UTF-8 byte string to JSON (fast path)\n"},
658 "escape a UTF-8 byte string to JSON (fast path)\n"},
659 {"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"},
659 {"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"},
660 {"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"},
660 {"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"},
661 {"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"},
661 {"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"},
662 {"fm1readmarkers", fm1readmarkers, METH_VARARGS,
662 {"fm1readmarkers", fm1readmarkers, METH_VARARGS,
663 "parse v1 obsolete markers\n"},
663 "parse v1 obsolete markers\n"},
664 {NULL, NULL}};
664 {NULL, NULL}};
665
665
666 void dirs_module_init(PyObject *mod);
666 void dirs_module_init(PyObject *mod);
667 void manifest_module_init(PyObject *mod);
667 void manifest_module_init(PyObject *mod);
668 void revlog_module_init(PyObject *mod);
668 void revlog_module_init(PyObject *mod);
669
669
670 static const int version = 13;
670 static const int version = 14;
671
671
672 static void module_init(PyObject *mod)
672 static void module_init(PyObject *mod)
673 {
673 {
674 PyObject *capsule = NULL;
674 PyObject *capsule = NULL;
675 PyModule_AddIntConstant(mod, "version", version);
675 PyModule_AddIntConstant(mod, "version", version);
676
676
677 /* This module constant has two purposes. First, it lets us unit test
677 /* This module constant has two purposes. First, it lets us unit test
678 * the ImportError raised without hard-coding any error text. This
678 * the ImportError raised without hard-coding any error text. This
679 * means we can change the text in the future without breaking tests,
679 * means we can change the text in the future without breaking tests,
680 * even across changesets without a recompile. Second, its presence
680 * even across changesets without a recompile. Second, its presence
681 * can be used to determine whether the version-checking logic is
681 * can be used to determine whether the version-checking logic is
682 * present, which also helps in testing across changesets without a
682 * present, which also helps in testing across changesets without a
683 * recompile. Note that this means the pure-Python version of parsers
683 * recompile. Note that this means the pure-Python version of parsers
684 * should not have this module constant. */
684 * should not have this module constant. */
685 PyModule_AddStringConstant(mod, "versionerrortext", versionerrortext);
685 PyModule_AddStringConstant(mod, "versionerrortext", versionerrortext);
686
686
687 dirs_module_init(mod);
687 dirs_module_init(mod);
688 manifest_module_init(mod);
688 manifest_module_init(mod);
689 revlog_module_init(mod);
689 revlog_module_init(mod);
690
690
691 capsule = PyCapsule_New(
691 capsule = PyCapsule_New(
692 make_dirstate_tuple,
692 make_dirstate_tuple,
693 "mercurial.cext.parsers.make_dirstate_tuple_CAPI", NULL);
693 "mercurial.cext.parsers.make_dirstate_tuple_CAPI", NULL);
694 if (capsule != NULL)
694 if (capsule != NULL)
695 PyModule_AddObject(mod, "make_dirstate_tuple_CAPI", capsule);
695 PyModule_AddObject(mod, "make_dirstate_tuple_CAPI", capsule);
696
696
697 if (PyType_Ready(&dirstateTupleType) < 0) {
697 if (PyType_Ready(&dirstateTupleType) < 0) {
698 return;
698 return;
699 }
699 }
700 Py_INCREF(&dirstateTupleType);
700 Py_INCREF(&dirstateTupleType);
701 PyModule_AddObject(mod, "dirstatetuple",
701 PyModule_AddObject(mod, "dirstatetuple",
702 (PyObject *)&dirstateTupleType);
702 (PyObject *)&dirstateTupleType);
703 }
703 }
704
704
705 static int check_python_version(void)
705 static int check_python_version(void)
706 {
706 {
707 PyObject *sys = PyImport_ImportModule("sys"), *ver;
707 PyObject *sys = PyImport_ImportModule("sys"), *ver;
708 long hexversion;
708 long hexversion;
709 if (!sys) {
709 if (!sys) {
710 return -1;
710 return -1;
711 }
711 }
712 ver = PyObject_GetAttrString(sys, "hexversion");
712 ver = PyObject_GetAttrString(sys, "hexversion");
713 Py_DECREF(sys);
713 Py_DECREF(sys);
714 if (!ver) {
714 if (!ver) {
715 return -1;
715 return -1;
716 }
716 }
717 hexversion = PyInt_AsLong(ver);
717 hexversion = PyInt_AsLong(ver);
718 Py_DECREF(ver);
718 Py_DECREF(ver);
719 /* sys.hexversion is a 32-bit number by default, so the -1 case
719 /* sys.hexversion is a 32-bit number by default, so the -1 case
720 * should only occur in unusual circumstances (e.g. if sys.hexversion
720 * should only occur in unusual circumstances (e.g. if sys.hexversion
721 * is manually set to an invalid value). */
721 * is manually set to an invalid value). */
722 if ((hexversion == -1) || (hexversion >> 16 != PY_VERSION_HEX >> 16)) {
722 if ((hexversion == -1) || (hexversion >> 16 != PY_VERSION_HEX >> 16)) {
723 PyErr_Format(PyExc_ImportError,
723 PyErr_Format(PyExc_ImportError,
724 "%s: The Mercurial extension "
724 "%s: The Mercurial extension "
725 "modules were compiled with Python " PY_VERSION
725 "modules were compiled with Python " PY_VERSION
726 ", but "
726 ", but "
727 "Mercurial is currently using Python with "
727 "Mercurial is currently using Python with "
728 "sys.hexversion=%ld: "
728 "sys.hexversion=%ld: "
729 "Python %s\n at: %s",
729 "Python %s\n at: %s",
730 versionerrortext, hexversion, Py_GetVersion(),
730 versionerrortext, hexversion, Py_GetVersion(),
731 Py_GetProgramFullPath());
731 Py_GetProgramFullPath());
732 return -1;
732 return -1;
733 }
733 }
734 return 0;
734 return 0;
735 }
735 }
736
736
737 #ifdef IS_PY3K
737 #ifdef IS_PY3K
738 static struct PyModuleDef parsers_module = {PyModuleDef_HEAD_INIT, "parsers",
738 static struct PyModuleDef parsers_module = {PyModuleDef_HEAD_INIT, "parsers",
739 parsers_doc, -1, methods};
739 parsers_doc, -1, methods};
740
740
741 PyMODINIT_FUNC PyInit_parsers(void)
741 PyMODINIT_FUNC PyInit_parsers(void)
742 {
742 {
743 PyObject *mod;
743 PyObject *mod;
744
744
745 if (check_python_version() == -1)
745 if (check_python_version() == -1)
746 return NULL;
746 return NULL;
747 mod = PyModule_Create(&parsers_module);
747 mod = PyModule_Create(&parsers_module);
748 module_init(mod);
748 module_init(mod);
749 return mod;
749 return mod;
750 }
750 }
751 #else
751 #else
752 PyMODINIT_FUNC initparsers(void)
752 PyMODINIT_FUNC initparsers(void)
753 {
753 {
754 PyObject *mod;
754 PyObject *mod;
755
755
756 if (check_python_version() == -1) {
756 if (check_python_version() == -1) {
757 return;
757 return;
758 }
758 }
759 mod = Py_InitModule3("parsers", methods, parsers_doc);
759 mod = Py_InitModule3("parsers", methods, parsers_doc);
760 module_init(mod);
760 module_init(mod);
761 }
761 }
762 #endif
762 #endif
@@ -1,3041 +1,3051 b''
1 /*
1 /*
2 parsers.c - efficient content parsing
2 parsers.c - efficient content parsing
3
3
4 Copyright 2008 Matt Mackall <mpm@selenic.com> and others
4 Copyright 2008 Matt Mackall <mpm@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
18
19 #include "bitmanipulation.h"
19 #include "bitmanipulation.h"
20 #include "charencode.h"
20 #include "charencode.h"
21 #include "revlog.h"
21 #include "revlog.h"
22 #include "util.h"
22 #include "util.h"
23
23
24 #ifdef IS_PY3K
24 #ifdef IS_PY3K
25 /* The mapping of Python types is meant to be temporary to get Python
25 /* The mapping of Python types is meant to be temporary to get Python
26 * 3 to compile. We should remove this once Python 3 support is fully
26 * 3 to compile. We should remove this once Python 3 support is fully
27 * supported and proper types are used in the extensions themselves. */
27 * supported and proper types are used in the extensions themselves. */
28 #define PyInt_Check PyLong_Check
28 #define PyInt_Check PyLong_Check
29 #define PyInt_FromLong PyLong_FromLong
29 #define PyInt_FromLong PyLong_FromLong
30 #define PyInt_FromSsize_t PyLong_FromSsize_t
30 #define PyInt_FromSsize_t PyLong_FromSsize_t
31 #define PyInt_AsLong PyLong_AsLong
31 #define PyInt_AsLong PyLong_AsLong
32 #endif
32 #endif
33
33
34 typedef struct indexObjectStruct indexObject;
34 typedef struct indexObjectStruct indexObject;
35
35
36 typedef struct {
36 typedef struct {
37 int children[16];
37 int children[16];
38 } nodetreenode;
38 } nodetreenode;
39
39
40 /*
40 /*
41 * A base-16 trie for fast node->rev mapping.
41 * A base-16 trie for fast node->rev mapping.
42 *
42 *
43 * Positive value is index of the next node in the trie
43 * Positive value is index of the next node in the trie
44 * Negative value is a leaf: -(rev + 2)
44 * Negative value is a leaf: -(rev + 2)
45 * Zero is empty
45 * Zero is empty
46 */
46 */
47 typedef struct {
47 typedef struct {
48 indexObject *index;
48 indexObject *index;
49 nodetreenode *nodes;
49 nodetreenode *nodes;
50 unsigned length; /* # nodes in use */
50 unsigned length; /* # nodes in use */
51 unsigned capacity; /* # nodes allocated */
51 unsigned capacity; /* # nodes allocated */
52 int depth; /* maximum depth of tree */
52 int depth; /* maximum depth of tree */
53 int splits; /* # splits performed */
53 int splits; /* # splits performed */
54 } nodetree;
54 } nodetree;
55
55
56 typedef struct {
56 typedef struct {
57 PyObject_HEAD /* ; */
57 PyObject_HEAD /* ; */
58 nodetree nt;
58 nodetree nt;
59 } nodetreeObject;
59 } nodetreeObject;
60
60
61 /*
61 /*
62 * This class has two behaviors.
62 * This class has two behaviors.
63 *
63 *
64 * When used in a list-like way (with integer keys), we decode an
64 * When used in a list-like way (with integer keys), we decode an
65 * entry in a RevlogNG index file on demand. Our last entry is a
65 * entry in a RevlogNG index file on demand. Our last entry is a
66 * sentinel, always a nullid. We have limited support for
66 * sentinel, always a nullid. We have limited support for
67 * integer-keyed insert and delete, only at elements right before the
67 * integer-keyed insert and delete, only at elements right before the
68 * sentinel.
68 * sentinel.
69 *
69 *
70 * With string keys, we lazily perform a reverse mapping from node to
70 * With string keys, we lazily perform a reverse mapping from node to
71 * rev, using a base-16 trie.
71 * rev, using a base-16 trie.
72 */
72 */
73 struct indexObjectStruct {
73 struct indexObjectStruct {
74 PyObject_HEAD
74 PyObject_HEAD
75 /* Type-specific fields go here. */
75 /* Type-specific fields go here. */
76 PyObject *data; /* raw bytes of index */
76 PyObject *data; /* raw bytes of index */
77 Py_buffer buf; /* buffer of data */
77 Py_buffer buf; /* buffer of data */
78 PyObject **cache; /* cached tuples */
78 PyObject **cache; /* cached tuples */
79 const char **offsets; /* populated on demand */
79 const char **offsets; /* populated on demand */
80 Py_ssize_t raw_length; /* original number of elements */
80 Py_ssize_t raw_length; /* original number of elements */
81 Py_ssize_t length; /* current number of elements */
81 Py_ssize_t length; /* current number of elements */
82 PyObject *added; /* populated on demand */
82 PyObject *added; /* populated on demand */
83 PyObject *headrevs; /* cache, invalidated on changes */
83 PyObject *headrevs; /* cache, invalidated on changes */
84 PyObject *filteredrevs; /* filtered revs set */
84 PyObject *filteredrevs; /* filtered revs set */
85 nodetree nt; /* base-16 trie */
85 nodetree nt; /* base-16 trie */
86 int ntinitialized; /* 0 or 1 */
86 int ntinitialized; /* 0 or 1 */
87 int ntrev; /* last rev scanned */
87 int ntrev; /* last rev scanned */
88 int ntlookups; /* # lookups */
88 int ntlookups; /* # lookups */
89 int ntmisses; /* # lookups that miss the cache */
89 int ntmisses; /* # lookups that miss the cache */
90 int inlined;
90 int inlined;
91 };
91 };
92
92
93 static Py_ssize_t index_length(const indexObject *self)
93 static Py_ssize_t index_length(const indexObject *self)
94 {
94 {
95 if (self->added == NULL)
95 if (self->added == NULL)
96 return self->length;
96 return self->length;
97 return self->length + PyList_GET_SIZE(self->added);
97 return self->length + PyList_GET_SIZE(self->added);
98 }
98 }
99
99
100 static PyObject *nullentry = NULL;
100 static PyObject *nullentry = NULL;
101 static const char nullid[20] = {0};
101 static const char nullid[20] = {0};
102 static const Py_ssize_t nullrev = -1;
102 static const Py_ssize_t nullrev = -1;
103
103
104 static Py_ssize_t inline_scan(indexObject *self, const char **offsets);
104 static Py_ssize_t inline_scan(indexObject *self, const char **offsets);
105
105
106 #if LONG_MAX == 0x7fffffffL
106 #if LONG_MAX == 0x7fffffffL
107 static const char *const tuple_format = PY23("Kiiiiiis#", "Kiiiiiiy#");
107 static const char *const tuple_format = PY23("Kiiiiiis#", "Kiiiiiiy#");
108 #else
108 #else
109 static const char *const tuple_format = PY23("kiiiiiis#", "kiiiiiiy#");
109 static const char *const tuple_format = PY23("kiiiiiis#", "kiiiiiiy#");
110 #endif
110 #endif
111
111
112 /* A RevlogNG v1 index entry is 64 bytes long. */
112 /* A RevlogNG v1 index entry is 64 bytes long. */
113 static const long v1_hdrsize = 64;
113 static const long v1_hdrsize = 64;
114
114
115 static void raise_revlog_error(void)
115 static void raise_revlog_error(void)
116 {
116 {
117 PyObject *mod = NULL, *dict = NULL, *errclass = NULL;
117 PyObject *mod = NULL, *dict = NULL, *errclass = NULL;
118
118
119 mod = PyImport_ImportModule("mercurial.error");
119 mod = PyImport_ImportModule("mercurial.error");
120 if (mod == NULL) {
120 if (mod == NULL) {
121 goto cleanup;
121 goto cleanup;
122 }
122 }
123
123
124 dict = PyModule_GetDict(mod);
124 dict = PyModule_GetDict(mod);
125 if (dict == NULL) {
125 if (dict == NULL) {
126 goto cleanup;
126 goto cleanup;
127 }
127 }
128 Py_INCREF(dict);
128 Py_INCREF(dict);
129
129
130 errclass = PyDict_GetItemString(dict, "RevlogError");
130 errclass = PyDict_GetItemString(dict, "RevlogError");
131 if (errclass == NULL) {
131 if (errclass == NULL) {
132 PyErr_SetString(PyExc_SystemError,
132 PyErr_SetString(PyExc_SystemError,
133 "could not find RevlogError");
133 "could not find RevlogError");
134 goto cleanup;
134 goto cleanup;
135 }
135 }
136
136
137 /* value of exception is ignored by callers */
137 /* value of exception is ignored by callers */
138 PyErr_SetString(errclass, "RevlogError");
138 PyErr_SetString(errclass, "RevlogError");
139
139
140 cleanup:
140 cleanup:
141 Py_XDECREF(dict);
141 Py_XDECREF(dict);
142 Py_XDECREF(mod);
142 Py_XDECREF(mod);
143 }
143 }
144
144
145 /*
145 /*
146 * Return a pointer to the beginning of a RevlogNG record.
146 * Return a pointer to the beginning of a RevlogNG record.
147 */
147 */
148 static const char *index_deref(indexObject *self, Py_ssize_t pos)
148 static const char *index_deref(indexObject *self, Py_ssize_t pos)
149 {
149 {
150 if (self->inlined && pos > 0) {
150 if (self->inlined && pos > 0) {
151 if (self->offsets == NULL) {
151 if (self->offsets == NULL) {
152 self->offsets = PyMem_Malloc(self->raw_length *
152 self->offsets = PyMem_Malloc(self->raw_length *
153 sizeof(*self->offsets));
153 sizeof(*self->offsets));
154 if (self->offsets == NULL)
154 if (self->offsets == NULL)
155 return (const char *)PyErr_NoMemory();
155 return (const char *)PyErr_NoMemory();
156 inline_scan(self, self->offsets);
156 inline_scan(self, self->offsets);
157 }
157 }
158 return self->offsets[pos];
158 return self->offsets[pos];
159 }
159 }
160
160
161 return (const char *)(self->buf.buf) + pos * v1_hdrsize;
161 return (const char *)(self->buf.buf) + pos * v1_hdrsize;
162 }
162 }
163
163
164 /*
164 /*
165 * Get parents of the given rev.
165 * Get parents of the given rev.
166 *
166 *
167 * The specified rev must be valid and must not be nullrev. A returned
167 * The specified rev must be valid and must not be nullrev. A returned
168 * parent revision may be nullrev, but is guaranteed to be in valid range.
168 * parent revision may be nullrev, but is guaranteed to be in valid range.
169 */
169 */
170 static inline int index_get_parents(indexObject *self, Py_ssize_t rev, int *ps,
170 static inline int index_get_parents(indexObject *self, Py_ssize_t rev, int *ps,
171 int maxrev)
171 int maxrev)
172 {
172 {
173 if (rev >= self->length) {
173 if (rev >= self->length) {
174 long tmp;
174 long tmp;
175 PyObject *tuple =
175 PyObject *tuple =
176 PyList_GET_ITEM(self->added, rev - self->length);
176 PyList_GET_ITEM(self->added, rev - self->length);
177 if (!pylong_to_long(PyTuple_GET_ITEM(tuple, 5), &tmp)) {
177 if (!pylong_to_long(PyTuple_GET_ITEM(tuple, 5), &tmp)) {
178 return -1;
178 return -1;
179 }
179 }
180 ps[0] = (int)tmp;
180 ps[0] = (int)tmp;
181 if (!pylong_to_long(PyTuple_GET_ITEM(tuple, 6), &tmp)) {
181 if (!pylong_to_long(PyTuple_GET_ITEM(tuple, 6), &tmp)) {
182 return -1;
182 return -1;
183 }
183 }
184 ps[1] = (int)tmp;
184 ps[1] = (int)tmp;
185 } else {
185 } else {
186 const char *data = index_deref(self, rev);
186 const char *data = index_deref(self, rev);
187 ps[0] = getbe32(data + 24);
187 ps[0] = getbe32(data + 24);
188 ps[1] = getbe32(data + 28);
188 ps[1] = getbe32(data + 28);
189 }
189 }
190 /* If index file is corrupted, ps[] may point to invalid revisions. So
190 /* If index file is corrupted, ps[] may point to invalid revisions. So
191 * there is a risk of buffer overflow to trust them unconditionally. */
191 * there is a risk of buffer overflow to trust them unconditionally. */
192 if (ps[0] < -1 || ps[0] > maxrev || ps[1] < -1 || ps[1] > maxrev) {
192 if (ps[0] < -1 || ps[0] > maxrev || ps[1] < -1 || ps[1] > maxrev) {
193 PyErr_SetString(PyExc_ValueError, "parent out of range");
193 PyErr_SetString(PyExc_ValueError, "parent out of range");
194 return -1;
194 return -1;
195 }
195 }
196 return 0;
196 return 0;
197 }
197 }
198
198
199 /*
199 /*
200 * Get parents of the given rev.
200 * Get parents of the given rev.
201 *
201 *
202 * If the specified rev is out of range, IndexError will be raised. If the
202 * If the specified rev is out of range, IndexError will be raised. If the
203 * revlog entry is corrupted, ValueError may be raised.
203 * revlog entry is corrupted, ValueError may be raised.
204 *
204 *
205 * Returns 0 on success or -1 on failure.
205 * Returns 0 on success or -1 on failure.
206 */
206 */
207 int HgRevlogIndex_GetParents(PyObject *op, int rev, int *ps)
207 int HgRevlogIndex_GetParents(PyObject *op, int rev, int *ps)
208 {
208 {
209 int tiprev;
209 int tiprev;
210 if (!op || !HgRevlogIndex_Check(op) || !ps) {
210 if (!op || !HgRevlogIndex_Check(op) || !ps) {
211 PyErr_BadInternalCall();
211 PyErr_BadInternalCall();
212 return -1;
212 return -1;
213 }
213 }
214 tiprev = (int)index_length((indexObject *)op) - 1;
214 tiprev = (int)index_length((indexObject *)op) - 1;
215 if (rev < -1 || rev > tiprev) {
215 if (rev < -1 || rev > tiprev) {
216 PyErr_Format(PyExc_IndexError, "rev out of range: %d", rev);
216 PyErr_Format(PyExc_IndexError, "rev out of range: %d", rev);
217 return -1;
217 return -1;
218 } else if (rev == -1) {
218 } else if (rev == -1) {
219 ps[0] = ps[1] = -1;
219 ps[0] = ps[1] = -1;
220 return 0;
220 return 0;
221 } else {
221 } else {
222 return index_get_parents((indexObject *)op, rev, ps, tiprev);
222 return index_get_parents((indexObject *)op, rev, ps, tiprev);
223 }
223 }
224 }
224 }
225
225
226 static inline int64_t index_get_start(indexObject *self, Py_ssize_t rev)
226 static inline int64_t index_get_start(indexObject *self, Py_ssize_t rev)
227 {
227 {
228 uint64_t offset;
228 uint64_t offset;
229 if (rev == nullrev) {
229 if (rev == nullrev) {
230 return 0;
230 return 0;
231 }
231 }
232 if (rev >= self->length) {
232 if (rev >= self->length) {
233 PyObject *tuple;
233 PyObject *tuple;
234 PyObject *pylong;
234 PyObject *pylong;
235 PY_LONG_LONG tmp;
235 PY_LONG_LONG tmp;
236 tuple = PyList_GET_ITEM(self->added, rev - self->length);
236 tuple = PyList_GET_ITEM(self->added, rev - self->length);
237 pylong = PyTuple_GET_ITEM(tuple, 0);
237 pylong = PyTuple_GET_ITEM(tuple, 0);
238 tmp = PyLong_AsLongLong(pylong);
238 tmp = PyLong_AsLongLong(pylong);
239 if (tmp == -1 && PyErr_Occurred()) {
239 if (tmp == -1 && PyErr_Occurred()) {
240 return -1;
240 return -1;
241 }
241 }
242 if (tmp < 0) {
242 if (tmp < 0) {
243 PyErr_Format(PyExc_OverflowError,
243 PyErr_Format(PyExc_OverflowError,
244 "revlog entry size out of bound (%lld)",
244 "revlog entry size out of bound (%lld)",
245 (long long)tmp);
245 (long long)tmp);
246 return -1;
246 return -1;
247 }
247 }
248 offset = (uint64_t)tmp;
248 offset = (uint64_t)tmp;
249 } else {
249 } else {
250 const char *data = index_deref(self, rev);
250 const char *data = index_deref(self, rev);
251 offset = getbe32(data + 4);
251 offset = getbe32(data + 4);
252 if (rev == 0) {
252 if (rev == 0) {
253 /* mask out version number for the first entry */
253 /* mask out version number for the first entry */
254 offset &= 0xFFFF;
254 offset &= 0xFFFF;
255 } else {
255 } else {
256 uint32_t offset_high = getbe32(data);
256 uint32_t offset_high = getbe32(data);
257 offset |= ((uint64_t)offset_high) << 32;
257 offset |= ((uint64_t)offset_high) << 32;
258 }
258 }
259 }
259 }
260 return (int64_t)(offset >> 16);
260 return (int64_t)(offset >> 16);
261 }
261 }
262
262
263 static inline int index_get_length(indexObject *self, Py_ssize_t rev)
263 static inline int index_get_length(indexObject *self, Py_ssize_t rev)
264 {
264 {
265 if (rev == nullrev) {
265 if (rev == nullrev) {
266 return 0;
266 return 0;
267 }
267 }
268 if (rev >= self->length) {
268 if (rev >= self->length) {
269 PyObject *tuple;
269 PyObject *tuple;
270 PyObject *pylong;
270 PyObject *pylong;
271 long ret;
271 long ret;
272 tuple = PyList_GET_ITEM(self->added, rev - self->length);
272 tuple = PyList_GET_ITEM(self->added, rev - self->length);
273 pylong = PyTuple_GET_ITEM(tuple, 1);
273 pylong = PyTuple_GET_ITEM(tuple, 1);
274 ret = PyInt_AsLong(pylong);
274 ret = PyInt_AsLong(pylong);
275 if (ret == -1 && PyErr_Occurred()) {
275 if (ret == -1 && PyErr_Occurred()) {
276 return -1;
276 return -1;
277 }
277 }
278 if (ret < 0 || ret > (long)INT_MAX) {
278 if (ret < 0 || ret > (long)INT_MAX) {
279 PyErr_Format(PyExc_OverflowError,
279 PyErr_Format(PyExc_OverflowError,
280 "revlog entry size out of bound (%ld)",
280 "revlog entry size out of bound (%ld)",
281 ret);
281 ret);
282 return -1;
282 return -1;
283 }
283 }
284 return (int)ret;
284 return (int)ret;
285 } else {
285 } else {
286 const char *data = index_deref(self, rev);
286 const char *data = index_deref(self, rev);
287 int tmp = (int)getbe32(data + 8);
287 int tmp = (int)getbe32(data + 8);
288 if (tmp < 0) {
288 if (tmp < 0) {
289 PyErr_Format(PyExc_OverflowError,
289 PyErr_Format(PyExc_OverflowError,
290 "revlog entry size out of bound (%d)",
290 "revlog entry size out of bound (%d)",
291 tmp);
291 tmp);
292 return -1;
292 return -1;
293 }
293 }
294 return tmp;
294 return tmp;
295 }
295 }
296 }
296 }
297
297
298 /*
298 /*
299 * RevlogNG format (all in big endian, data may be inlined):
299 * RevlogNG format (all in big endian, data may be inlined):
300 * 6 bytes: offset
300 * 6 bytes: offset
301 * 2 bytes: flags
301 * 2 bytes: flags
302 * 4 bytes: compressed length
302 * 4 bytes: compressed length
303 * 4 bytes: uncompressed length
303 * 4 bytes: uncompressed length
304 * 4 bytes: base revision
304 * 4 bytes: base revision
305 * 4 bytes: link revision
305 * 4 bytes: link revision
306 * 4 bytes: parent 1 revision
306 * 4 bytes: parent 1 revision
307 * 4 bytes: parent 2 revision
307 * 4 bytes: parent 2 revision
308 * 32 bytes: nodeid (only 20 bytes used)
308 * 32 bytes: nodeid (only 20 bytes used)
309 */
309 */
310 static PyObject *index_get(indexObject *self, Py_ssize_t pos)
310 static PyObject *index_get(indexObject *self, Py_ssize_t pos)
311 {
311 {
312 uint64_t offset_flags;
312 uint64_t offset_flags;
313 int comp_len, uncomp_len, base_rev, link_rev, parent_1, parent_2;
313 int comp_len, uncomp_len, base_rev, link_rev, parent_1, parent_2;
314 const char *c_node_id;
314 const char *c_node_id;
315 const char *data;
315 const char *data;
316 Py_ssize_t length = index_length(self);
316 Py_ssize_t length = index_length(self);
317 PyObject *entry;
317 PyObject *entry;
318
318
319 if (pos == nullrev) {
319 if (pos == nullrev) {
320 Py_INCREF(nullentry);
320 Py_INCREF(nullentry);
321 return nullentry;
321 return nullentry;
322 }
322 }
323
323
324 if (pos < 0 || pos >= length) {
324 if (pos < 0 || pos >= length) {
325 PyErr_SetString(PyExc_IndexError, "revlog index out of range");
325 PyErr_SetString(PyExc_IndexError, "revlog index out of range");
326 return NULL;
326 return NULL;
327 }
327 }
328
328
329 if (pos >= self->length) {
329 if (pos >= self->length) {
330 PyObject *obj;
330 PyObject *obj;
331 obj = PyList_GET_ITEM(self->added, pos - self->length);
331 obj = PyList_GET_ITEM(self->added, pos - self->length);
332 Py_INCREF(obj);
332 Py_INCREF(obj);
333 return obj;
333 return obj;
334 }
334 }
335
335
336 if (self->cache) {
336 if (self->cache) {
337 if (self->cache[pos]) {
337 if (self->cache[pos]) {
338 Py_INCREF(self->cache[pos]);
338 Py_INCREF(self->cache[pos]);
339 return self->cache[pos];
339 return self->cache[pos];
340 }
340 }
341 } else {
341 } else {
342 self->cache = calloc(self->raw_length, sizeof(PyObject *));
342 self->cache = calloc(self->raw_length, sizeof(PyObject *));
343 if (self->cache == NULL)
343 if (self->cache == NULL)
344 return PyErr_NoMemory();
344 return PyErr_NoMemory();
345 }
345 }
346
346
347 data = index_deref(self, pos);
347 data = index_deref(self, pos);
348 if (data == NULL)
348 if (data == NULL)
349 return NULL;
349 return NULL;
350
350
351 offset_flags = getbe32(data + 4);
351 offset_flags = getbe32(data + 4);
352 if (pos == 0) /* mask out version number for the first entry */
352 if (pos == 0) /* mask out version number for the first entry */
353 offset_flags &= 0xFFFF;
353 offset_flags &= 0xFFFF;
354 else {
354 else {
355 uint32_t offset_high = getbe32(data);
355 uint32_t offset_high = getbe32(data);
356 offset_flags |= ((uint64_t)offset_high) << 32;
356 offset_flags |= ((uint64_t)offset_high) << 32;
357 }
357 }
358
358
359 comp_len = getbe32(data + 8);
359 comp_len = getbe32(data + 8);
360 uncomp_len = getbe32(data + 12);
360 uncomp_len = getbe32(data + 12);
361 base_rev = getbe32(data + 16);
361 base_rev = getbe32(data + 16);
362 link_rev = getbe32(data + 20);
362 link_rev = getbe32(data + 20);
363 parent_1 = getbe32(data + 24);
363 parent_1 = getbe32(data + 24);
364 parent_2 = getbe32(data + 28);
364 parent_2 = getbe32(data + 28);
365 c_node_id = data + 32;
365 c_node_id = data + 32;
366
366
367 entry = Py_BuildValue(tuple_format, offset_flags, comp_len, uncomp_len,
367 entry = Py_BuildValue(tuple_format, offset_flags, comp_len, uncomp_len,
368 base_rev, link_rev, parent_1, parent_2, c_node_id,
368 base_rev, link_rev, parent_1, parent_2, c_node_id,
369 (Py_ssize_t)20);
369 (Py_ssize_t)20);
370
370
371 if (entry) {
371 if (entry) {
372 PyObject_GC_UnTrack(entry);
372 PyObject_GC_UnTrack(entry);
373 Py_INCREF(entry);
373 Py_INCREF(entry);
374 }
374 }
375
375
376 self->cache[pos] = entry;
376 self->cache[pos] = entry;
377
377
378 return entry;
378 return entry;
379 }
379 }
380
380
381 /*
381 /*
382 * Return the 20-byte SHA of the node corresponding to the given rev.
382 * Return the 20-byte SHA of the node corresponding to the given rev.
383 */
383 */
384 static const char *index_node(indexObject *self, Py_ssize_t pos)
384 static const char *index_node(indexObject *self, Py_ssize_t pos)
385 {
385 {
386 Py_ssize_t length = index_length(self);
386 Py_ssize_t length = index_length(self);
387 const char *data;
387 const char *data;
388
388
389 if (pos == nullrev)
389 if (pos == nullrev)
390 return nullid;
390 return nullid;
391
391
392 if (pos >= length)
392 if (pos >= length)
393 return NULL;
393 return NULL;
394
394
395 if (pos >= self->length) {
395 if (pos >= self->length) {
396 PyObject *tuple, *str;
396 PyObject *tuple, *str;
397 tuple = PyList_GET_ITEM(self->added, pos - self->length);
397 tuple = PyList_GET_ITEM(self->added, pos - self->length);
398 str = PyTuple_GetItem(tuple, 7);
398 str = PyTuple_GetItem(tuple, 7);
399 return str ? PyBytes_AS_STRING(str) : NULL;
399 return str ? PyBytes_AS_STRING(str) : NULL;
400 }
400 }
401
401
402 data = index_deref(self, pos);
402 data = index_deref(self, pos);
403 return data ? data + 32 : NULL;
403 return data ? data + 32 : NULL;
404 }
404 }
405
405
406 /*
406 /*
407 * Return the 20-byte SHA of the node corresponding to the given rev. The
407 * Return the 20-byte SHA of the node corresponding to the given rev. The
408 * rev is assumed to be existing. If not, an exception is set.
408 * rev is assumed to be existing. If not, an exception is set.
409 */
409 */
410 static const char *index_node_existing(indexObject *self, Py_ssize_t pos)
410 static const char *index_node_existing(indexObject *self, Py_ssize_t pos)
411 {
411 {
412 const char *node = index_node(self, pos);
412 const char *node = index_node(self, pos);
413 if (node == NULL) {
413 if (node == NULL) {
414 PyErr_Format(PyExc_IndexError, "could not access rev %d",
414 PyErr_Format(PyExc_IndexError, "could not access rev %d",
415 (int)pos);
415 (int)pos);
416 }
416 }
417 return node;
417 return node;
418 }
418 }
419
419
420 static int nt_insert(nodetree *self, const char *node, int rev);
420 static int nt_insert(nodetree *self, const char *node, int rev);
421
421
422 static int node_check(PyObject *obj, char **node)
422 static int node_check(PyObject *obj, char **node)
423 {
423 {
424 Py_ssize_t nodelen;
424 Py_ssize_t nodelen;
425 if (PyBytes_AsStringAndSize(obj, node, &nodelen) == -1)
425 if (PyBytes_AsStringAndSize(obj, node, &nodelen) == -1)
426 return -1;
426 return -1;
427 if (nodelen == 20)
427 if (nodelen == 20)
428 return 0;
428 return 0;
429 PyErr_SetString(PyExc_ValueError, "20-byte hash required");
429 PyErr_SetString(PyExc_ValueError, "20-byte hash required");
430 return -1;
430 return -1;
431 }
431 }
432
432
433 static PyObject *index_append(indexObject *self, PyObject *obj)
433 static PyObject *index_append(indexObject *self, PyObject *obj)
434 {
434 {
435 char *node;
435 char *node;
436 Py_ssize_t len;
436 Py_ssize_t len;
437
437
438 if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 8) {
438 if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 8) {
439 PyErr_SetString(PyExc_TypeError, "8-tuple required");
439 PyErr_SetString(PyExc_TypeError, "8-tuple required");
440 return NULL;
440 return NULL;
441 }
441 }
442
442
443 if (node_check(PyTuple_GET_ITEM(obj, 7), &node) == -1)
443 if (node_check(PyTuple_GET_ITEM(obj, 7), &node) == -1)
444 return NULL;
444 return NULL;
445
445
446 len = index_length(self);
446 len = index_length(self);
447
447
448 if (self->added == NULL) {
448 if (self->added == NULL) {
449 self->added = PyList_New(0);
449 self->added = PyList_New(0);
450 if (self->added == NULL)
450 if (self->added == NULL)
451 return NULL;
451 return NULL;
452 }
452 }
453
453
454 if (PyList_Append(self->added, obj) == -1)
454 if (PyList_Append(self->added, obj) == -1)
455 return NULL;
455 return NULL;
456
456
457 if (self->ntinitialized)
457 if (self->ntinitialized)
458 nt_insert(&self->nt, node, (int)len);
458 nt_insert(&self->nt, node, (int)len);
459
459
460 Py_CLEAR(self->headrevs);
460 Py_CLEAR(self->headrevs);
461 Py_RETURN_NONE;
461 Py_RETURN_NONE;
462 }
462 }
463
463
464 static PyObject *index_stats(indexObject *self)
464 static PyObject *index_stats(indexObject *self)
465 {
465 {
466 PyObject *obj = PyDict_New();
466 PyObject *obj = PyDict_New();
467 PyObject *s = NULL;
467 PyObject *s = NULL;
468 PyObject *t = NULL;
468 PyObject *t = NULL;
469
469
470 if (obj == NULL)
470 if (obj == NULL)
471 return NULL;
471 return NULL;
472
472
473 #define istat(__n, __d) \
473 #define istat(__n, __d) \
474 do { \
474 do { \
475 s = PyBytes_FromString(__d); \
475 s = PyBytes_FromString(__d); \
476 t = PyInt_FromSsize_t(self->__n); \
476 t = PyInt_FromSsize_t(self->__n); \
477 if (!s || !t) \
477 if (!s || !t) \
478 goto bail; \
478 goto bail; \
479 if (PyDict_SetItem(obj, s, t) == -1) \
479 if (PyDict_SetItem(obj, s, t) == -1) \
480 goto bail; \
480 goto bail; \
481 Py_CLEAR(s); \
481 Py_CLEAR(s); \
482 Py_CLEAR(t); \
482 Py_CLEAR(t); \
483 } while (0)
483 } while (0)
484
484
485 if (self->added) {
485 if (self->added) {
486 Py_ssize_t len = PyList_GET_SIZE(self->added);
486 Py_ssize_t len = PyList_GET_SIZE(self->added);
487 s = PyBytes_FromString("index entries added");
487 s = PyBytes_FromString("index entries added");
488 t = PyInt_FromSsize_t(len);
488 t = PyInt_FromSsize_t(len);
489 if (!s || !t)
489 if (!s || !t)
490 goto bail;
490 goto bail;
491 if (PyDict_SetItem(obj, s, t) == -1)
491 if (PyDict_SetItem(obj, s, t) == -1)
492 goto bail;
492 goto bail;
493 Py_CLEAR(s);
493 Py_CLEAR(s);
494 Py_CLEAR(t);
494 Py_CLEAR(t);
495 }
495 }
496
496
497 if (self->raw_length != self->length)
497 if (self->raw_length != self->length)
498 istat(raw_length, "revs on disk");
498 istat(raw_length, "revs on disk");
499 istat(length, "revs in memory");
499 istat(length, "revs in memory");
500 istat(ntlookups, "node trie lookups");
500 istat(ntlookups, "node trie lookups");
501 istat(ntmisses, "node trie misses");
501 istat(ntmisses, "node trie misses");
502 istat(ntrev, "node trie last rev scanned");
502 istat(ntrev, "node trie last rev scanned");
503 if (self->ntinitialized) {
503 if (self->ntinitialized) {
504 istat(nt.capacity, "node trie capacity");
504 istat(nt.capacity, "node trie capacity");
505 istat(nt.depth, "node trie depth");
505 istat(nt.depth, "node trie depth");
506 istat(nt.length, "node trie count");
506 istat(nt.length, "node trie count");
507 istat(nt.splits, "node trie splits");
507 istat(nt.splits, "node trie splits");
508 }
508 }
509
509
510 #undef istat
510 #undef istat
511
511
512 return obj;
512 return obj;
513
513
514 bail:
514 bail:
515 Py_XDECREF(obj);
515 Py_XDECREF(obj);
516 Py_XDECREF(s);
516 Py_XDECREF(s);
517 Py_XDECREF(t);
517 Py_XDECREF(t);
518 return NULL;
518 return NULL;
519 }
519 }
520
520
521 /*
521 /*
522 * When we cache a list, we want to be sure the caller can't mutate
522 * When we cache a list, we want to be sure the caller can't mutate
523 * the cached copy.
523 * the cached copy.
524 */
524 */
525 static PyObject *list_copy(PyObject *list)
525 static PyObject *list_copy(PyObject *list)
526 {
526 {
527 Py_ssize_t len = PyList_GET_SIZE(list);
527 Py_ssize_t len = PyList_GET_SIZE(list);
528 PyObject *newlist = PyList_New(len);
528 PyObject *newlist = PyList_New(len);
529 Py_ssize_t i;
529 Py_ssize_t i;
530
530
531 if (newlist == NULL)
531 if (newlist == NULL)
532 return NULL;
532 return NULL;
533
533
534 for (i = 0; i < len; i++) {
534 for (i = 0; i < len; i++) {
535 PyObject *obj = PyList_GET_ITEM(list, i);
535 PyObject *obj = PyList_GET_ITEM(list, i);
536 Py_INCREF(obj);
536 Py_INCREF(obj);
537 PyList_SET_ITEM(newlist, i, obj);
537 PyList_SET_ITEM(newlist, i, obj);
538 }
538 }
539
539
540 return newlist;
540 return newlist;
541 }
541 }
542
542
543 static int check_filter(PyObject *filter, Py_ssize_t arg)
543 static int check_filter(PyObject *filter, Py_ssize_t arg)
544 {
544 {
545 if (filter) {
545 if (filter) {
546 PyObject *arglist, *result;
546 PyObject *arglist, *result;
547 int isfiltered;
547 int isfiltered;
548
548
549 arglist = Py_BuildValue("(n)", arg);
549 arglist = Py_BuildValue("(n)", arg);
550 if (!arglist) {
550 if (!arglist) {
551 return -1;
551 return -1;
552 }
552 }
553
553
554 result = PyEval_CallObject(filter, arglist);
554 result = PyEval_CallObject(filter, arglist);
555 Py_DECREF(arglist);
555 Py_DECREF(arglist);
556 if (!result) {
556 if (!result) {
557 return -1;
557 return -1;
558 }
558 }
559
559
560 /* PyObject_IsTrue returns 1 if true, 0 if false, -1 if error,
560 /* PyObject_IsTrue returns 1 if true, 0 if false, -1 if error,
561 * same as this function, so we can just return it directly.*/
561 * same as this function, so we can just return it directly.*/
562 isfiltered = PyObject_IsTrue(result);
562 isfiltered = PyObject_IsTrue(result);
563 Py_DECREF(result);
563 Py_DECREF(result);
564 return isfiltered;
564 return isfiltered;
565 } else {
565 } else {
566 return 0;
566 return 0;
567 }
567 }
568 }
568 }
569
569
570 static Py_ssize_t add_roots_get_min(indexObject *self, PyObject *list,
570 static Py_ssize_t add_roots_get_min(indexObject *self, PyObject *list,
571 Py_ssize_t marker, char *phases)
571 Py_ssize_t marker, char *phases)
572 {
572 {
573 PyObject *iter = NULL;
573 PyObject *iter = NULL;
574 PyObject *iter_item = NULL;
574 PyObject *iter_item = NULL;
575 Py_ssize_t min_idx = index_length(self) + 2;
575 Py_ssize_t min_idx = index_length(self) + 2;
576 long iter_item_long;
576 long iter_item_long;
577
577
578 if (PyList_GET_SIZE(list) != 0) {
578 if (PyList_GET_SIZE(list) != 0) {
579 iter = PyObject_GetIter(list);
579 iter = PyObject_GetIter(list);
580 if (iter == NULL)
580 if (iter == NULL)
581 return -2;
581 return -2;
582 while ((iter_item = PyIter_Next(iter))) {
582 while ((iter_item = PyIter_Next(iter))) {
583 if (!pylong_to_long(iter_item, &iter_item_long)) {
583 if (!pylong_to_long(iter_item, &iter_item_long)) {
584 Py_DECREF(iter_item);
584 Py_DECREF(iter_item);
585 return -2;
585 return -2;
586 }
586 }
587 Py_DECREF(iter_item);
587 Py_DECREF(iter_item);
588 if (iter_item_long < min_idx)
588 if (iter_item_long < min_idx)
589 min_idx = iter_item_long;
589 min_idx = iter_item_long;
590 phases[iter_item_long] = (char)marker;
590 phases[iter_item_long] = (char)marker;
591 }
591 }
592 Py_DECREF(iter);
592 Py_DECREF(iter);
593 }
593 }
594
594
595 return min_idx;
595 return min_idx;
596 }
596 }
597
597
598 static inline void set_phase_from_parents(char *phases, int parent_1,
598 static inline void set_phase_from_parents(char *phases, int parent_1,
599 int parent_2, Py_ssize_t i)
599 int parent_2, Py_ssize_t i)
600 {
600 {
601 if (parent_1 >= 0 && phases[parent_1] > phases[i])
601 if (parent_1 >= 0 && phases[parent_1] > phases[i])
602 phases[i] = phases[parent_1];
602 phases[i] = phases[parent_1];
603 if (parent_2 >= 0 && phases[parent_2] > phases[i])
603 if (parent_2 >= 0 && phases[parent_2] > phases[i])
604 phases[i] = phases[parent_2];
604 phases[i] = phases[parent_2];
605 }
605 }
606
606
607 static PyObject *reachableroots2(indexObject *self, PyObject *args)
607 static PyObject *reachableroots2(indexObject *self, PyObject *args)
608 {
608 {
609
609
610 /* Input */
610 /* Input */
611 long minroot;
611 long minroot;
612 PyObject *includepatharg = NULL;
612 PyObject *includepatharg = NULL;
613 int includepath = 0;
613 int includepath = 0;
614 /* heads and roots are lists */
614 /* heads and roots are lists */
615 PyObject *heads = NULL;
615 PyObject *heads = NULL;
616 PyObject *roots = NULL;
616 PyObject *roots = NULL;
617 PyObject *reachable = NULL;
617 PyObject *reachable = NULL;
618
618
619 PyObject *val;
619 PyObject *val;
620 Py_ssize_t len = index_length(self);
620 Py_ssize_t len = index_length(self);
621 long revnum;
621 long revnum;
622 Py_ssize_t k;
622 Py_ssize_t k;
623 Py_ssize_t i;
623 Py_ssize_t i;
624 Py_ssize_t l;
624 Py_ssize_t l;
625 int r;
625 int r;
626 int parents[2];
626 int parents[2];
627
627
628 /* Internal data structure:
628 /* Internal data structure:
629 * tovisit: array of length len+1 (all revs + nullrev), filled upto
629 * tovisit: array of length len+1 (all revs + nullrev), filled upto
630 * lentovisit
630 * lentovisit
631 *
631 *
632 * revstates: array of length len+1 (all revs + nullrev) */
632 * revstates: array of length len+1 (all revs + nullrev) */
633 int *tovisit = NULL;
633 int *tovisit = NULL;
634 long lentovisit = 0;
634 long lentovisit = 0;
635 enum { RS_SEEN = 1, RS_ROOT = 2, RS_REACHABLE = 4 };
635 enum { RS_SEEN = 1, RS_ROOT = 2, RS_REACHABLE = 4 };
636 char *revstates = NULL;
636 char *revstates = NULL;
637
637
638 /* Get arguments */
638 /* Get arguments */
639 if (!PyArg_ParseTuple(args, "lO!O!O!", &minroot, &PyList_Type, &heads,
639 if (!PyArg_ParseTuple(args, "lO!O!O!", &minroot, &PyList_Type, &heads,
640 &PyList_Type, &roots, &PyBool_Type,
640 &PyList_Type, &roots, &PyBool_Type,
641 &includepatharg))
641 &includepatharg))
642 goto bail;
642 goto bail;
643
643
644 if (includepatharg == Py_True)
644 if (includepatharg == Py_True)
645 includepath = 1;
645 includepath = 1;
646
646
647 /* Initialize return set */
647 /* Initialize return set */
648 reachable = PyList_New(0);
648 reachable = PyList_New(0);
649 if (reachable == NULL)
649 if (reachable == NULL)
650 goto bail;
650 goto bail;
651
651
652 /* Initialize internal datastructures */
652 /* Initialize internal datastructures */
653 tovisit = (int *)malloc((len + 1) * sizeof(int));
653 tovisit = (int *)malloc((len + 1) * sizeof(int));
654 if (tovisit == NULL) {
654 if (tovisit == NULL) {
655 PyErr_NoMemory();
655 PyErr_NoMemory();
656 goto bail;
656 goto bail;
657 }
657 }
658
658
659 revstates = (char *)calloc(len + 1, 1);
659 revstates = (char *)calloc(len + 1, 1);
660 if (revstates == NULL) {
660 if (revstates == NULL) {
661 PyErr_NoMemory();
661 PyErr_NoMemory();
662 goto bail;
662 goto bail;
663 }
663 }
664
664
665 l = PyList_GET_SIZE(roots);
665 l = PyList_GET_SIZE(roots);
666 for (i = 0; i < l; i++) {
666 for (i = 0; i < l; i++) {
667 revnum = PyInt_AsLong(PyList_GET_ITEM(roots, i));
667 revnum = PyInt_AsLong(PyList_GET_ITEM(roots, i));
668 if (revnum == -1 && PyErr_Occurred())
668 if (revnum == -1 && PyErr_Occurred())
669 goto bail;
669 goto bail;
670 /* If root is out of range, e.g. wdir(), it must be unreachable
670 /* If root is out of range, e.g. wdir(), it must be unreachable
671 * from heads. So we can just ignore it. */
671 * from heads. So we can just ignore it. */
672 if (revnum + 1 < 0 || revnum + 1 >= len + 1)
672 if (revnum + 1 < 0 || revnum + 1 >= len + 1)
673 continue;
673 continue;
674 revstates[revnum + 1] |= RS_ROOT;
674 revstates[revnum + 1] |= RS_ROOT;
675 }
675 }
676
676
677 /* Populate tovisit with all the heads */
677 /* Populate tovisit with all the heads */
678 l = PyList_GET_SIZE(heads);
678 l = PyList_GET_SIZE(heads);
679 for (i = 0; i < l; i++) {
679 for (i = 0; i < l; i++) {
680 revnum = PyInt_AsLong(PyList_GET_ITEM(heads, i));
680 revnum = PyInt_AsLong(PyList_GET_ITEM(heads, i));
681 if (revnum == -1 && PyErr_Occurred())
681 if (revnum == -1 && PyErr_Occurred())
682 goto bail;
682 goto bail;
683 if (revnum + 1 < 0 || revnum + 1 >= len + 1) {
683 if (revnum + 1 < 0 || revnum + 1 >= len + 1) {
684 PyErr_SetString(PyExc_IndexError, "head out of range");
684 PyErr_SetString(PyExc_IndexError, "head out of range");
685 goto bail;
685 goto bail;
686 }
686 }
687 if (!(revstates[revnum + 1] & RS_SEEN)) {
687 if (!(revstates[revnum + 1] & RS_SEEN)) {
688 tovisit[lentovisit++] = (int)revnum;
688 tovisit[lentovisit++] = (int)revnum;
689 revstates[revnum + 1] |= RS_SEEN;
689 revstates[revnum + 1] |= RS_SEEN;
690 }
690 }
691 }
691 }
692
692
693 /* Visit the tovisit list and find the reachable roots */
693 /* Visit the tovisit list and find the reachable roots */
694 k = 0;
694 k = 0;
695 while (k < lentovisit) {
695 while (k < lentovisit) {
696 /* Add the node to reachable if it is a root*/
696 /* Add the node to reachable if it is a root*/
697 revnum = tovisit[k++];
697 revnum = tovisit[k++];
698 if (revstates[revnum + 1] & RS_ROOT) {
698 if (revstates[revnum + 1] & RS_ROOT) {
699 revstates[revnum + 1] |= RS_REACHABLE;
699 revstates[revnum + 1] |= RS_REACHABLE;
700 val = PyInt_FromLong(revnum);
700 val = PyInt_FromLong(revnum);
701 if (val == NULL)
701 if (val == NULL)
702 goto bail;
702 goto bail;
703 r = PyList_Append(reachable, val);
703 r = PyList_Append(reachable, val);
704 Py_DECREF(val);
704 Py_DECREF(val);
705 if (r < 0)
705 if (r < 0)
706 goto bail;
706 goto bail;
707 if (includepath == 0)
707 if (includepath == 0)
708 continue;
708 continue;
709 }
709 }
710
710
711 /* Add its parents to the list of nodes to visit */
711 /* Add its parents to the list of nodes to visit */
712 if (revnum == nullrev)
712 if (revnum == nullrev)
713 continue;
713 continue;
714 r = index_get_parents(self, revnum, parents, (int)len - 1);
714 r = index_get_parents(self, revnum, parents, (int)len - 1);
715 if (r < 0)
715 if (r < 0)
716 goto bail;
716 goto bail;
717 for (i = 0; i < 2; i++) {
717 for (i = 0; i < 2; i++) {
718 if (!(revstates[parents[i] + 1] & RS_SEEN) &&
718 if (!(revstates[parents[i] + 1] & RS_SEEN) &&
719 parents[i] >= minroot) {
719 parents[i] >= minroot) {
720 tovisit[lentovisit++] = parents[i];
720 tovisit[lentovisit++] = parents[i];
721 revstates[parents[i] + 1] |= RS_SEEN;
721 revstates[parents[i] + 1] |= RS_SEEN;
722 }
722 }
723 }
723 }
724 }
724 }
725
725
726 /* Find all the nodes in between the roots we found and the heads
726 /* Find all the nodes in between the roots we found and the heads
727 * and add them to the reachable set */
727 * and add them to the reachable set */
728 if (includepath == 1) {
728 if (includepath == 1) {
729 long minidx = minroot;
729 long minidx = minroot;
730 if (minidx < 0)
730 if (minidx < 0)
731 minidx = 0;
731 minidx = 0;
732 for (i = minidx; i < len; i++) {
732 for (i = minidx; i < len; i++) {
733 if (!(revstates[i + 1] & RS_SEEN))
733 if (!(revstates[i + 1] & RS_SEEN))
734 continue;
734 continue;
735 r = index_get_parents(self, i, parents, (int)len - 1);
735 r = index_get_parents(self, i, parents, (int)len - 1);
736 /* Corrupted index file, error is set from
736 /* Corrupted index file, error is set from
737 * index_get_parents */
737 * index_get_parents */
738 if (r < 0)
738 if (r < 0)
739 goto bail;
739 goto bail;
740 if (((revstates[parents[0] + 1] |
740 if (((revstates[parents[0] + 1] |
741 revstates[parents[1] + 1]) &
741 revstates[parents[1] + 1]) &
742 RS_REACHABLE) &&
742 RS_REACHABLE) &&
743 !(revstates[i + 1] & RS_REACHABLE)) {
743 !(revstates[i + 1] & RS_REACHABLE)) {
744 revstates[i + 1] |= RS_REACHABLE;
744 revstates[i + 1] |= RS_REACHABLE;
745 val = PyInt_FromSsize_t(i);
745 val = PyInt_FromSsize_t(i);
746 if (val == NULL)
746 if (val == NULL)
747 goto bail;
747 goto bail;
748 r = PyList_Append(reachable, val);
748 r = PyList_Append(reachable, val);
749 Py_DECREF(val);
749 Py_DECREF(val);
750 if (r < 0)
750 if (r < 0)
751 goto bail;
751 goto bail;
752 }
752 }
753 }
753 }
754 }
754 }
755
755
756 free(revstates);
756 free(revstates);
757 free(tovisit);
757 free(tovisit);
758 return reachable;
758 return reachable;
759 bail:
759 bail:
760 Py_XDECREF(reachable);
760 Py_XDECREF(reachable);
761 free(revstates);
761 free(revstates);
762 free(tovisit);
762 free(tovisit);
763 return NULL;
763 return NULL;
764 }
764 }
765
765
766 static PyObject *compute_phases_map_sets(indexObject *self, PyObject *args)
766 static PyObject *compute_phases_map_sets(indexObject *self, PyObject *args)
767 {
767 {
768 PyObject *roots = Py_None;
768 PyObject *roots = Py_None;
769 PyObject *ret = NULL;
769 PyObject *ret = NULL;
770 PyObject *phasessize = NULL;
770 PyObject *phasessize = NULL;
771 PyObject *phaseroots = NULL;
771 PyObject *phaseroots = NULL;
772 PyObject *phaseset = NULL;
772 PyObject *phaseset = NULL;
773 PyObject *phasessetlist = NULL;
773 PyObject *phasessetlist = NULL;
774 PyObject *rev = NULL;
774 PyObject *rev = NULL;
775 Py_ssize_t len = index_length(self);
775 Py_ssize_t len = index_length(self);
776 Py_ssize_t numphase = 0;
776 Py_ssize_t numphase = 0;
777 Py_ssize_t minrevallphases = 0;
777 Py_ssize_t minrevallphases = 0;
778 Py_ssize_t minrevphase = 0;
778 Py_ssize_t minrevphase = 0;
779 Py_ssize_t i = 0;
779 Py_ssize_t i = 0;
780 char *phases = NULL;
780 char *phases = NULL;
781 long phase;
781 long phase;
782
782
783 if (!PyArg_ParseTuple(args, "O", &roots))
783 if (!PyArg_ParseTuple(args, "O", &roots))
784 goto done;
784 goto done;
785 if (roots == NULL || !PyList_Check(roots)) {
785 if (roots == NULL || !PyList_Check(roots)) {
786 PyErr_SetString(PyExc_TypeError, "roots must be a list");
786 PyErr_SetString(PyExc_TypeError, "roots must be a list");
787 goto done;
787 goto done;
788 }
788 }
789
789
790 phases = calloc(
790 phases = calloc(
791 len, 1); /* phase per rev: {0: public, 1: draft, 2: secret} */
791 len, 1); /* phase per rev: {0: public, 1: draft, 2: secret} */
792 if (phases == NULL) {
792 if (phases == NULL) {
793 PyErr_NoMemory();
793 PyErr_NoMemory();
794 goto done;
794 goto done;
795 }
795 }
796 /* Put the phase information of all the roots in phases */
796 /* Put the phase information of all the roots in phases */
797 numphase = PyList_GET_SIZE(roots) + 1;
797 numphase = PyList_GET_SIZE(roots) + 1;
798 minrevallphases = len + 1;
798 minrevallphases = len + 1;
799 phasessetlist = PyList_New(numphase);
799 phasessetlist = PyList_New(numphase);
800 if (phasessetlist == NULL)
800 if (phasessetlist == NULL)
801 goto done;
801 goto done;
802
802
803 PyList_SET_ITEM(phasessetlist, 0, Py_None);
803 PyList_SET_ITEM(phasessetlist, 0, Py_None);
804 Py_INCREF(Py_None);
804 Py_INCREF(Py_None);
805
805
806 for (i = 0; i < numphase - 1; i++) {
806 for (i = 0; i < numphase - 1; i++) {
807 phaseroots = PyList_GET_ITEM(roots, i);
807 phaseroots = PyList_GET_ITEM(roots, i);
808 phaseset = PySet_New(NULL);
808 phaseset = PySet_New(NULL);
809 if (phaseset == NULL)
809 if (phaseset == NULL)
810 goto release;
810 goto release;
811 PyList_SET_ITEM(phasessetlist, i + 1, phaseset);
811 PyList_SET_ITEM(phasessetlist, i + 1, phaseset);
812 if (!PyList_Check(phaseroots)) {
812 if (!PyList_Check(phaseroots)) {
813 PyErr_SetString(PyExc_TypeError,
813 PyErr_SetString(PyExc_TypeError,
814 "roots item must be a list");
814 "roots item must be a list");
815 goto release;
815 goto release;
816 }
816 }
817 minrevphase =
817 minrevphase =
818 add_roots_get_min(self, phaseroots, i + 1, phases);
818 add_roots_get_min(self, phaseroots, i + 1, phases);
819 if (minrevphase == -2) /* Error from add_roots_get_min */
819 if (minrevphase == -2) /* Error from add_roots_get_min */
820 goto release;
820 goto release;
821 minrevallphases = MIN(minrevallphases, minrevphase);
821 minrevallphases = MIN(minrevallphases, minrevphase);
822 }
822 }
823 /* Propagate the phase information from the roots to the revs */
823 /* Propagate the phase information from the roots to the revs */
824 if (minrevallphases != -1) {
824 if (minrevallphases != -1) {
825 int parents[2];
825 int parents[2];
826 for (i = minrevallphases; i < len; i++) {
826 for (i = minrevallphases; i < len; i++) {
827 if (index_get_parents(self, i, parents, (int)len - 1) <
827 if (index_get_parents(self, i, parents, (int)len - 1) <
828 0)
828 0)
829 goto release;
829 goto release;
830 set_phase_from_parents(phases, parents[0], parents[1],
830 set_phase_from_parents(phases, parents[0], parents[1],
831 i);
831 i);
832 }
832 }
833 }
833 }
834 /* Transform phase list to a python list */
834 /* Transform phase list to a python list */
835 phasessize = PyInt_FromSsize_t(len);
835 phasessize = PyInt_FromSsize_t(len);
836 if (phasessize == NULL)
836 if (phasessize == NULL)
837 goto release;
837 goto release;
838 for (i = 0; i < len; i++) {
838 for (i = 0; i < len; i++) {
839 phase = phases[i];
839 phase = phases[i];
840 /* We only store the sets of phase for non public phase, the
840 /* We only store the sets of phase for non public phase, the
841 * public phase is computed as a difference */
841 * public phase is computed as a difference */
842 if (phase != 0) {
842 if (phase != 0) {
843 phaseset = PyList_GET_ITEM(phasessetlist, phase);
843 phaseset = PyList_GET_ITEM(phasessetlist, phase);
844 rev = PyInt_FromSsize_t(i);
844 rev = PyInt_FromSsize_t(i);
845 if (rev == NULL)
845 if (rev == NULL)
846 goto release;
846 goto release;
847 PySet_Add(phaseset, rev);
847 PySet_Add(phaseset, rev);
848 Py_XDECREF(rev);
848 Py_XDECREF(rev);
849 }
849 }
850 }
850 }
851 ret = PyTuple_Pack(2, phasessize, phasessetlist);
851 ret = PyTuple_Pack(2, phasessize, phasessetlist);
852
852
853 release:
853 release:
854 Py_XDECREF(phasessize);
854 Py_XDECREF(phasessize);
855 Py_XDECREF(phasessetlist);
855 Py_XDECREF(phasessetlist);
856 done:
856 done:
857 free(phases);
857 free(phases);
858 return ret;
858 return ret;
859 }
859 }
860
860
861 static PyObject *index_headrevs(indexObject *self, PyObject *args)
861 static PyObject *index_headrevs(indexObject *self, PyObject *args)
862 {
862 {
863 Py_ssize_t i, j, len;
863 Py_ssize_t i, j, len;
864 char *nothead = NULL;
864 char *nothead = NULL;
865 PyObject *heads = NULL;
865 PyObject *heads = NULL;
866 PyObject *filter = NULL;
866 PyObject *filter = NULL;
867 PyObject *filteredrevs = Py_None;
867 PyObject *filteredrevs = Py_None;
868
868
869 if (!PyArg_ParseTuple(args, "|O", &filteredrevs)) {
869 if (!PyArg_ParseTuple(args, "|O", &filteredrevs)) {
870 return NULL;
870 return NULL;
871 }
871 }
872
872
873 if (self->headrevs && filteredrevs == self->filteredrevs)
873 if (self->headrevs && filteredrevs == self->filteredrevs)
874 return list_copy(self->headrevs);
874 return list_copy(self->headrevs);
875
875
876 Py_DECREF(self->filteredrevs);
876 Py_DECREF(self->filteredrevs);
877 self->filteredrevs = filteredrevs;
877 self->filteredrevs = filteredrevs;
878 Py_INCREF(filteredrevs);
878 Py_INCREF(filteredrevs);
879
879
880 if (filteredrevs != Py_None) {
880 if (filteredrevs != Py_None) {
881 filter = PyObject_GetAttrString(filteredrevs, "__contains__");
881 filter = PyObject_GetAttrString(filteredrevs, "__contains__");
882 if (!filter) {
882 if (!filter) {
883 PyErr_SetString(
883 PyErr_SetString(
884 PyExc_TypeError,
884 PyExc_TypeError,
885 "filteredrevs has no attribute __contains__");
885 "filteredrevs has no attribute __contains__");
886 goto bail;
886 goto bail;
887 }
887 }
888 }
888 }
889
889
890 len = index_length(self);
890 len = index_length(self);
891 heads = PyList_New(0);
891 heads = PyList_New(0);
892 if (heads == NULL)
892 if (heads == NULL)
893 goto bail;
893 goto bail;
894 if (len == 0) {
894 if (len == 0) {
895 PyObject *nullid = PyInt_FromLong(-1);
895 PyObject *nullid = PyInt_FromLong(-1);
896 if (nullid == NULL || PyList_Append(heads, nullid) == -1) {
896 if (nullid == NULL || PyList_Append(heads, nullid) == -1) {
897 Py_XDECREF(nullid);
897 Py_XDECREF(nullid);
898 goto bail;
898 goto bail;
899 }
899 }
900 goto done;
900 goto done;
901 }
901 }
902
902
903 nothead = calloc(len, 1);
903 nothead = calloc(len, 1);
904 if (nothead == NULL) {
904 if (nothead == NULL) {
905 PyErr_NoMemory();
905 PyErr_NoMemory();
906 goto bail;
906 goto bail;
907 }
907 }
908
908
909 for (i = len - 1; i >= 0; i--) {
909 for (i = len - 1; i >= 0; i--) {
910 int isfiltered;
910 int isfiltered;
911 int parents[2];
911 int parents[2];
912
912
913 /* If nothead[i] == 1, it means we've seen an unfiltered child
913 /* If nothead[i] == 1, it means we've seen an unfiltered child
914 * of this node already, and therefore this node is not
914 * of this node already, and therefore this node is not
915 * filtered. So we can skip the expensive check_filter step.
915 * filtered. So we can skip the expensive check_filter step.
916 */
916 */
917 if (nothead[i] != 1) {
917 if (nothead[i] != 1) {
918 isfiltered = check_filter(filter, i);
918 isfiltered = check_filter(filter, i);
919 if (isfiltered == -1) {
919 if (isfiltered == -1) {
920 PyErr_SetString(PyExc_TypeError,
920 PyErr_SetString(PyExc_TypeError,
921 "unable to check filter");
921 "unable to check filter");
922 goto bail;
922 goto bail;
923 }
923 }
924
924
925 if (isfiltered) {
925 if (isfiltered) {
926 nothead[i] = 1;
926 nothead[i] = 1;
927 continue;
927 continue;
928 }
928 }
929 }
929 }
930
930
931 if (index_get_parents(self, i, parents, (int)len - 1) < 0)
931 if (index_get_parents(self, i, parents, (int)len - 1) < 0)
932 goto bail;
932 goto bail;
933 for (j = 0; j < 2; j++) {
933 for (j = 0; j < 2; j++) {
934 if (parents[j] >= 0)
934 if (parents[j] >= 0)
935 nothead[parents[j]] = 1;
935 nothead[parents[j]] = 1;
936 }
936 }
937 }
937 }
938
938
939 for (i = 0; i < len; i++) {
939 for (i = 0; i < len; i++) {
940 PyObject *head;
940 PyObject *head;
941
941
942 if (nothead[i])
942 if (nothead[i])
943 continue;
943 continue;
944 head = PyInt_FromSsize_t(i);
944 head = PyInt_FromSsize_t(i);
945 if (head == NULL || PyList_Append(heads, head) == -1) {
945 if (head == NULL || PyList_Append(heads, head) == -1) {
946 Py_XDECREF(head);
946 Py_XDECREF(head);
947 goto bail;
947 goto bail;
948 }
948 }
949 }
949 }
950
950
951 done:
951 done:
952 self->headrevs = heads;
952 self->headrevs = heads;
953 Py_XDECREF(filter);
953 Py_XDECREF(filter);
954 free(nothead);
954 free(nothead);
955 return list_copy(self->headrevs);
955 return list_copy(self->headrevs);
956 bail:
956 bail:
957 Py_XDECREF(filter);
957 Py_XDECREF(filter);
958 Py_XDECREF(heads);
958 Py_XDECREF(heads);
959 free(nothead);
959 free(nothead);
960 return NULL;
960 return NULL;
961 }
961 }
962
962
963 /**
963 /**
964 * Obtain the base revision index entry.
964 * Obtain the base revision index entry.
965 *
965 *
966 * Callers must ensure that rev >= 0 or illegal memory access may occur.
966 * Callers must ensure that rev >= 0 or illegal memory access may occur.
967 */
967 */
968 static inline int index_baserev(indexObject *self, int rev)
968 static inline int index_baserev(indexObject *self, int rev)
969 {
969 {
970 const char *data;
970 const char *data;
971 int result;
971 int result;
972
972
973 if (rev >= self->length) {
973 if (rev >= self->length) {
974 PyObject *tuple =
974 PyObject *tuple =
975 PyList_GET_ITEM(self->added, rev - self->length);
975 PyList_GET_ITEM(self->added, rev - self->length);
976 long ret;
976 long ret;
977 if (!pylong_to_long(PyTuple_GET_ITEM(tuple, 3), &ret)) {
977 if (!pylong_to_long(PyTuple_GET_ITEM(tuple, 3), &ret)) {
978 return -2;
978 return -2;
979 }
979 }
980 result = (int)ret;
980 result = (int)ret;
981 } else {
981 } else {
982 data = index_deref(self, rev);
982 data = index_deref(self, rev);
983 if (data == NULL) {
983 if (data == NULL) {
984 return -2;
984 return -2;
985 }
985 }
986
986
987 result = getbe32(data + 16);
987 result = getbe32(data + 16);
988 }
988 }
989 if (result > rev) {
989 if (result > rev) {
990 PyErr_Format(
990 PyErr_Format(
991 PyExc_ValueError,
991 PyExc_ValueError,
992 "corrupted revlog, revision base above revision: %d, %d",
992 "corrupted revlog, revision base above revision: %d, %d",
993 rev, result);
993 rev, result);
994 return -2;
994 return -2;
995 }
995 }
996 if (result < -1) {
996 if (result < -1) {
997 PyErr_Format(
997 PyErr_Format(
998 PyExc_ValueError,
998 PyExc_ValueError,
999 "corrupted revlog, revision base out of range: %d, %d", rev,
999 "corrupted revlog, revision base out of range: %d, %d", rev,
1000 result);
1000 result);
1001 return -2;
1001 return -2;
1002 }
1002 }
1003 return result;
1003 return result;
1004 }
1004 }
1005
1005
1006 /**
1006 /**
1007 * Find if a revision is a snapshot or not
1007 * Find if a revision is a snapshot or not
1008 *
1008 *
1009 * Only relevant for sparse-revlog case.
1009 * Only relevant for sparse-revlog case.
1010 * Callers must ensure that rev is in a valid range.
1010 * Callers must ensure that rev is in a valid range.
1011 */
1011 */
1012 static int index_issnapshotrev(indexObject *self, Py_ssize_t rev)
1012 static int index_issnapshotrev(indexObject *self, Py_ssize_t rev)
1013 {
1013 {
1014 int ps[2];
1014 int ps[2];
1015 Py_ssize_t base;
1015 Py_ssize_t base;
1016 while (rev >= 0) {
1016 while (rev >= 0) {
1017 base = (Py_ssize_t)index_baserev(self, rev);
1017 base = (Py_ssize_t)index_baserev(self, rev);
1018 if (base == rev) {
1018 if (base == rev) {
1019 base = -1;
1019 base = -1;
1020 }
1020 }
1021 if (base == -2) {
1021 if (base == -2) {
1022 assert(PyErr_Occurred());
1022 assert(PyErr_Occurred());
1023 return -1;
1023 return -1;
1024 }
1024 }
1025 if (base == -1) {
1025 if (base == -1) {
1026 return 1;
1026 return 1;
1027 }
1027 }
1028 if (index_get_parents(self, rev, ps, (int)rev) < 0) {
1028 if (index_get_parents(self, rev, ps, (int)rev) < 0) {
1029 assert(PyErr_Occurred());
1029 assert(PyErr_Occurred());
1030 return -1;
1030 return -1;
1031 };
1031 };
1032 if (base == ps[0] || base == ps[1]) {
1032 if (base == ps[0] || base == ps[1]) {
1033 return 0;
1033 return 0;
1034 }
1034 }
1035 rev = base;
1035 rev = base;
1036 }
1036 }
1037 return rev == -1;
1037 return rev == -1;
1038 }
1038 }
1039
1039
1040 static PyObject *index_issnapshot(indexObject *self, PyObject *value)
1040 static PyObject *index_issnapshot(indexObject *self, PyObject *value)
1041 {
1041 {
1042 long rev;
1042 long rev;
1043 int issnap;
1043 int issnap;
1044 Py_ssize_t length = index_length(self);
1044 Py_ssize_t length = index_length(self);
1045
1045
1046 if (!pylong_to_long(value, &rev)) {
1046 if (!pylong_to_long(value, &rev)) {
1047 return NULL;
1047 return NULL;
1048 }
1048 }
1049 if (rev < -1 || rev >= length) {
1049 if (rev < -1 || rev >= length) {
1050 PyErr_Format(PyExc_ValueError, "revlog index out of range: %ld",
1050 PyErr_Format(PyExc_ValueError, "revlog index out of range: %ld",
1051 rev);
1051 rev);
1052 return NULL;
1052 return NULL;
1053 };
1053 };
1054 issnap = index_issnapshotrev(self, (Py_ssize_t)rev);
1054 issnap = index_issnapshotrev(self, (Py_ssize_t)rev);
1055 if (issnap < 0) {
1055 if (issnap < 0) {
1056 return NULL;
1056 return NULL;
1057 };
1057 };
1058 return PyBool_FromLong((long)issnap);
1058 return PyBool_FromLong((long)issnap);
1059 }
1059 }
1060
1060
1061 static PyObject *index_findsnapshots(indexObject *self, PyObject *args)
1061 static PyObject *index_findsnapshots(indexObject *self, PyObject *args)
1062 {
1062 {
1063 Py_ssize_t start_rev;
1063 Py_ssize_t start_rev;
1064 PyObject *cache;
1064 PyObject *cache;
1065 Py_ssize_t base;
1065 Py_ssize_t base;
1066 Py_ssize_t rev;
1066 Py_ssize_t rev;
1067 PyObject *key = NULL;
1067 PyObject *key = NULL;
1068 PyObject *value = NULL;
1068 PyObject *value = NULL;
1069 const Py_ssize_t length = index_length(self);
1069 const Py_ssize_t length = index_length(self);
1070 if (!PyArg_ParseTuple(args, "O!n", &PyDict_Type, &cache, &start_rev)) {
1070 if (!PyArg_ParseTuple(args, "O!n", &PyDict_Type, &cache, &start_rev)) {
1071 return NULL;
1071 return NULL;
1072 }
1072 }
1073 for (rev = start_rev; rev < length; rev++) {
1073 for (rev = start_rev; rev < length; rev++) {
1074 int issnap;
1074 int issnap;
1075 PyObject *allvalues = NULL;
1075 PyObject *allvalues = NULL;
1076 issnap = index_issnapshotrev(self, rev);
1076 issnap = index_issnapshotrev(self, rev);
1077 if (issnap < 0) {
1077 if (issnap < 0) {
1078 goto bail;
1078 goto bail;
1079 }
1079 }
1080 if (issnap == 0) {
1080 if (issnap == 0) {
1081 continue;
1081 continue;
1082 }
1082 }
1083 base = (Py_ssize_t)index_baserev(self, rev);
1083 base = (Py_ssize_t)index_baserev(self, rev);
1084 if (base == rev) {
1084 if (base == rev) {
1085 base = -1;
1085 base = -1;
1086 }
1086 }
1087 if (base == -2) {
1087 if (base == -2) {
1088 assert(PyErr_Occurred());
1088 assert(PyErr_Occurred());
1089 goto bail;
1089 goto bail;
1090 }
1090 }
1091 key = PyInt_FromSsize_t(base);
1091 key = PyInt_FromSsize_t(base);
1092 allvalues = PyDict_GetItem(cache, key);
1092 allvalues = PyDict_GetItem(cache, key);
1093 if (allvalues == NULL && PyErr_Occurred()) {
1093 if (allvalues == NULL && PyErr_Occurred()) {
1094 goto bail;
1094 goto bail;
1095 }
1095 }
1096 if (allvalues == NULL) {
1096 if (allvalues == NULL) {
1097 int r;
1097 int r;
1098 allvalues = PyList_New(0);
1098 allvalues = PyList_New(0);
1099 if (!allvalues) {
1099 if (!allvalues) {
1100 goto bail;
1100 goto bail;
1101 }
1101 }
1102 r = PyDict_SetItem(cache, key, allvalues);
1102 r = PyDict_SetItem(cache, key, allvalues);
1103 Py_DECREF(allvalues);
1103 Py_DECREF(allvalues);
1104 if (r < 0) {
1104 if (r < 0) {
1105 goto bail;
1105 goto bail;
1106 }
1106 }
1107 }
1107 }
1108 value = PyInt_FromSsize_t(rev);
1108 value = PyInt_FromSsize_t(rev);
1109 if (PyList_Append(allvalues, value)) {
1109 if (PyList_Append(allvalues, value)) {
1110 goto bail;
1110 goto bail;
1111 }
1111 }
1112 Py_CLEAR(key);
1112 Py_CLEAR(key);
1113 Py_CLEAR(value);
1113 Py_CLEAR(value);
1114 }
1114 }
1115 Py_RETURN_NONE;
1115 Py_RETURN_NONE;
1116 bail:
1116 bail:
1117 Py_XDECREF(key);
1117 Py_XDECREF(key);
1118 Py_XDECREF(value);
1118 Py_XDECREF(value);
1119 return NULL;
1119 return NULL;
1120 }
1120 }
1121
1121
1122 static PyObject *index_deltachain(indexObject *self, PyObject *args)
1122 static PyObject *index_deltachain(indexObject *self, PyObject *args)
1123 {
1123 {
1124 int rev, generaldelta;
1124 int rev, generaldelta;
1125 PyObject *stoparg;
1125 PyObject *stoparg;
1126 int stoprev, iterrev, baserev = -1;
1126 int stoprev, iterrev, baserev = -1;
1127 int stopped;
1127 int stopped;
1128 PyObject *chain = NULL, *result = NULL;
1128 PyObject *chain = NULL, *result = NULL;
1129 const Py_ssize_t length = index_length(self);
1129 const Py_ssize_t length = index_length(self);
1130
1130
1131 if (!PyArg_ParseTuple(args, "iOi", &rev, &stoparg, &generaldelta)) {
1131 if (!PyArg_ParseTuple(args, "iOi", &rev, &stoparg, &generaldelta)) {
1132 return NULL;
1132 return NULL;
1133 }
1133 }
1134
1134
1135 if (PyInt_Check(stoparg)) {
1135 if (PyInt_Check(stoparg)) {
1136 stoprev = (int)PyInt_AsLong(stoparg);
1136 stoprev = (int)PyInt_AsLong(stoparg);
1137 if (stoprev == -1 && PyErr_Occurred()) {
1137 if (stoprev == -1 && PyErr_Occurred()) {
1138 return NULL;
1138 return NULL;
1139 }
1139 }
1140 } else if (stoparg == Py_None) {
1140 } else if (stoparg == Py_None) {
1141 stoprev = -2;
1141 stoprev = -2;
1142 } else {
1142 } else {
1143 PyErr_SetString(PyExc_ValueError,
1143 PyErr_SetString(PyExc_ValueError,
1144 "stoprev must be integer or None");
1144 "stoprev must be integer or None");
1145 return NULL;
1145 return NULL;
1146 }
1146 }
1147
1147
1148 if (rev < 0 || rev >= length) {
1148 if (rev < 0 || rev >= length) {
1149 PyErr_SetString(PyExc_ValueError, "revlog index out of range");
1149 PyErr_SetString(PyExc_ValueError, "revlog index out of range");
1150 return NULL;
1150 return NULL;
1151 }
1151 }
1152
1152
1153 chain = PyList_New(0);
1153 chain = PyList_New(0);
1154 if (chain == NULL) {
1154 if (chain == NULL) {
1155 return NULL;
1155 return NULL;
1156 }
1156 }
1157
1157
1158 baserev = index_baserev(self, rev);
1158 baserev = index_baserev(self, rev);
1159
1159
1160 /* This should never happen. */
1160 /* This should never happen. */
1161 if (baserev <= -2) {
1161 if (baserev <= -2) {
1162 /* Error should be set by index_deref() */
1162 /* Error should be set by index_deref() */
1163 assert(PyErr_Occurred());
1163 assert(PyErr_Occurred());
1164 goto bail;
1164 goto bail;
1165 }
1165 }
1166
1166
1167 iterrev = rev;
1167 iterrev = rev;
1168
1168
1169 while (iterrev != baserev && iterrev != stoprev) {
1169 while (iterrev != baserev && iterrev != stoprev) {
1170 PyObject *value = PyInt_FromLong(iterrev);
1170 PyObject *value = PyInt_FromLong(iterrev);
1171 if (value == NULL) {
1171 if (value == NULL) {
1172 goto bail;
1172 goto bail;
1173 }
1173 }
1174 if (PyList_Append(chain, value)) {
1174 if (PyList_Append(chain, value)) {
1175 Py_DECREF(value);
1175 Py_DECREF(value);
1176 goto bail;
1176 goto bail;
1177 }
1177 }
1178 Py_DECREF(value);
1178 Py_DECREF(value);
1179
1179
1180 if (generaldelta) {
1180 if (generaldelta) {
1181 iterrev = baserev;
1181 iterrev = baserev;
1182 } else {
1182 } else {
1183 iterrev--;
1183 iterrev--;
1184 }
1184 }
1185
1185
1186 if (iterrev < 0) {
1186 if (iterrev < 0) {
1187 break;
1187 break;
1188 }
1188 }
1189
1189
1190 if (iterrev >= length) {
1190 if (iterrev >= length) {
1191 PyErr_SetString(PyExc_IndexError,
1191 PyErr_SetString(PyExc_IndexError,
1192 "revision outside index");
1192 "revision outside index");
1193 return NULL;
1193 return NULL;
1194 }
1194 }
1195
1195
1196 baserev = index_baserev(self, iterrev);
1196 baserev = index_baserev(self, iterrev);
1197
1197
1198 /* This should never happen. */
1198 /* This should never happen. */
1199 if (baserev <= -2) {
1199 if (baserev <= -2) {
1200 /* Error should be set by index_deref() */
1200 /* Error should be set by index_deref() */
1201 assert(PyErr_Occurred());
1201 assert(PyErr_Occurred());
1202 goto bail;
1202 goto bail;
1203 }
1203 }
1204 }
1204 }
1205
1205
1206 if (iterrev == stoprev) {
1206 if (iterrev == stoprev) {
1207 stopped = 1;
1207 stopped = 1;
1208 } else {
1208 } else {
1209 PyObject *value = PyInt_FromLong(iterrev);
1209 PyObject *value = PyInt_FromLong(iterrev);
1210 if (value == NULL) {
1210 if (value == NULL) {
1211 goto bail;
1211 goto bail;
1212 }
1212 }
1213 if (PyList_Append(chain, value)) {
1213 if (PyList_Append(chain, value)) {
1214 Py_DECREF(value);
1214 Py_DECREF(value);
1215 goto bail;
1215 goto bail;
1216 }
1216 }
1217 Py_DECREF(value);
1217 Py_DECREF(value);
1218
1218
1219 stopped = 0;
1219 stopped = 0;
1220 }
1220 }
1221
1221
1222 if (PyList_Reverse(chain)) {
1222 if (PyList_Reverse(chain)) {
1223 goto bail;
1223 goto bail;
1224 }
1224 }
1225
1225
1226 result = Py_BuildValue("OO", chain, stopped ? Py_True : Py_False);
1226 result = Py_BuildValue("OO", chain, stopped ? Py_True : Py_False);
1227 Py_DECREF(chain);
1227 Py_DECREF(chain);
1228 return result;
1228 return result;
1229
1229
1230 bail:
1230 bail:
1231 Py_DECREF(chain);
1231 Py_DECREF(chain);
1232 return NULL;
1232 return NULL;
1233 }
1233 }
1234
1234
1235 static inline int64_t
1235 static inline int64_t
1236 index_segment_span(indexObject *self, Py_ssize_t start_rev, Py_ssize_t end_rev)
1236 index_segment_span(indexObject *self, Py_ssize_t start_rev, Py_ssize_t end_rev)
1237 {
1237 {
1238 int64_t start_offset;
1238 int64_t start_offset;
1239 int64_t end_offset;
1239 int64_t end_offset;
1240 int end_size;
1240 int end_size;
1241 start_offset = index_get_start(self, start_rev);
1241 start_offset = index_get_start(self, start_rev);
1242 if (start_offset < 0) {
1242 if (start_offset < 0) {
1243 return -1;
1243 return -1;
1244 }
1244 }
1245 end_offset = index_get_start(self, end_rev);
1245 end_offset = index_get_start(self, end_rev);
1246 if (end_offset < 0) {
1246 if (end_offset < 0) {
1247 return -1;
1247 return -1;
1248 }
1248 }
1249 end_size = index_get_length(self, end_rev);
1249 end_size = index_get_length(self, end_rev);
1250 if (end_size < 0) {
1250 if (end_size < 0) {
1251 return -1;
1251 return -1;
1252 }
1252 }
1253 if (end_offset < start_offset) {
1253 if (end_offset < start_offset) {
1254 PyErr_Format(PyExc_ValueError,
1254 PyErr_Format(PyExc_ValueError,
1255 "corrupted revlog index: inconsistent offset "
1255 "corrupted revlog index: inconsistent offset "
1256 "between revisions (%zd) and (%zd)",
1256 "between revisions (%zd) and (%zd)",
1257 start_rev, end_rev);
1257 start_rev, end_rev);
1258 return -1;
1258 return -1;
1259 }
1259 }
1260 return (end_offset - start_offset) + (int64_t)end_size;
1260 return (end_offset - start_offset) + (int64_t)end_size;
1261 }
1261 }
1262
1262
1263 /* returns endidx so that revs[startidx:endidx] has no empty trailing revs */
1263 /* returns endidx so that revs[startidx:endidx] has no empty trailing revs */
1264 static Py_ssize_t trim_endidx(indexObject *self, const Py_ssize_t *revs,
1264 static Py_ssize_t trim_endidx(indexObject *self, const Py_ssize_t *revs,
1265 Py_ssize_t startidx, Py_ssize_t endidx)
1265 Py_ssize_t startidx, Py_ssize_t endidx)
1266 {
1266 {
1267 int length;
1267 int length;
1268 while (endidx > 1 && endidx > startidx) {
1268 while (endidx > 1 && endidx > startidx) {
1269 length = index_get_length(self, revs[endidx - 1]);
1269 length = index_get_length(self, revs[endidx - 1]);
1270 if (length < 0) {
1270 if (length < 0) {
1271 return -1;
1271 return -1;
1272 }
1272 }
1273 if (length != 0) {
1273 if (length != 0) {
1274 break;
1274 break;
1275 }
1275 }
1276 endidx -= 1;
1276 endidx -= 1;
1277 }
1277 }
1278 return endidx;
1278 return endidx;
1279 }
1279 }
1280
1280
1281 struct Gap {
1281 struct Gap {
1282 int64_t size;
1282 int64_t size;
1283 Py_ssize_t idx;
1283 Py_ssize_t idx;
1284 };
1284 };
1285
1285
1286 static int gap_compare(const void *left, const void *right)
1286 static int gap_compare(const void *left, const void *right)
1287 {
1287 {
1288 const struct Gap *l_left = ((const struct Gap *)left);
1288 const struct Gap *l_left = ((const struct Gap *)left);
1289 const struct Gap *l_right = ((const struct Gap *)right);
1289 const struct Gap *l_right = ((const struct Gap *)right);
1290 if (l_left->size < l_right->size) {
1290 if (l_left->size < l_right->size) {
1291 return -1;
1291 return -1;
1292 } else if (l_left->size > l_right->size) {
1292 } else if (l_left->size > l_right->size) {
1293 return 1;
1293 return 1;
1294 }
1294 }
1295 return 0;
1295 return 0;
1296 }
1296 }
1297 static int Py_ssize_t_compare(const void *left, const void *right)
1297 static int Py_ssize_t_compare(const void *left, const void *right)
1298 {
1298 {
1299 const Py_ssize_t l_left = *(const Py_ssize_t *)left;
1299 const Py_ssize_t l_left = *(const Py_ssize_t *)left;
1300 const Py_ssize_t l_right = *(const Py_ssize_t *)right;
1300 const Py_ssize_t l_right = *(const Py_ssize_t *)right;
1301 if (l_left < l_right) {
1301 if (l_left < l_right) {
1302 return -1;
1302 return -1;
1303 } else if (l_left > l_right) {
1303 } else if (l_left > l_right) {
1304 return 1;
1304 return 1;
1305 }
1305 }
1306 return 0;
1306 return 0;
1307 }
1307 }
1308
1308
1309 static PyObject *index_slicechunktodensity(indexObject *self, PyObject *args)
1309 static PyObject *index_slicechunktodensity(indexObject *self, PyObject *args)
1310 {
1310 {
1311 /* method arguments */
1311 /* method arguments */
1312 PyObject *list_revs = NULL; /* revisions in the chain */
1312 PyObject *list_revs = NULL; /* revisions in the chain */
1313 double targetdensity = 0; /* min density to achieve */
1313 double targetdensity = 0; /* min density to achieve */
1314 Py_ssize_t mingapsize = 0; /* threshold to ignore gaps */
1314 Py_ssize_t mingapsize = 0; /* threshold to ignore gaps */
1315
1315
1316 /* other core variables */
1316 /* other core variables */
1317 Py_ssize_t idxlen = index_length(self);
1317 Py_ssize_t idxlen = index_length(self);
1318 Py_ssize_t i; /* used for various iteration */
1318 Py_ssize_t i; /* used for various iteration */
1319 PyObject *result = NULL; /* the final return of the function */
1319 PyObject *result = NULL; /* the final return of the function */
1320
1320
1321 /* generic information about the delta chain being slice */
1321 /* generic information about the delta chain being slice */
1322 Py_ssize_t num_revs = 0; /* size of the full delta chain */
1322 Py_ssize_t num_revs = 0; /* size of the full delta chain */
1323 Py_ssize_t *revs = NULL; /* native array of revision in the chain */
1323 Py_ssize_t *revs = NULL; /* native array of revision in the chain */
1324 int64_t chainpayload = 0; /* sum of all delta in the chain */
1324 int64_t chainpayload = 0; /* sum of all delta in the chain */
1325 int64_t deltachainspan = 0; /* distance from first byte to last byte */
1325 int64_t deltachainspan = 0; /* distance from first byte to last byte */
1326
1326
1327 /* variable used for slicing the delta chain */
1327 /* variable used for slicing the delta chain */
1328 int64_t readdata = 0; /* amount of data currently planned to be read */
1328 int64_t readdata = 0; /* amount of data currently planned to be read */
1329 double density = 0; /* ration of payload data compared to read ones */
1329 double density = 0; /* ration of payload data compared to read ones */
1330 int64_t previous_end;
1330 int64_t previous_end;
1331 struct Gap *gaps = NULL; /* array of notable gap in the chain */
1331 struct Gap *gaps = NULL; /* array of notable gap in the chain */
1332 Py_ssize_t num_gaps =
1332 Py_ssize_t num_gaps =
1333 0; /* total number of notable gap recorded so far */
1333 0; /* total number of notable gap recorded so far */
1334 Py_ssize_t *selected_indices = NULL; /* indices of gap skipped over */
1334 Py_ssize_t *selected_indices = NULL; /* indices of gap skipped over */
1335 Py_ssize_t num_selected = 0; /* number of gaps skipped */
1335 Py_ssize_t num_selected = 0; /* number of gaps skipped */
1336 PyObject *chunk = NULL; /* individual slice */
1336 PyObject *chunk = NULL; /* individual slice */
1337 PyObject *allchunks = NULL; /* all slices */
1337 PyObject *allchunks = NULL; /* all slices */
1338 Py_ssize_t previdx;
1338 Py_ssize_t previdx;
1339
1339
1340 /* parsing argument */
1340 /* parsing argument */
1341 if (!PyArg_ParseTuple(args, "O!dn", &PyList_Type, &list_revs,
1341 if (!PyArg_ParseTuple(args, "O!dn", &PyList_Type, &list_revs,
1342 &targetdensity, &mingapsize)) {
1342 &targetdensity, &mingapsize)) {
1343 goto bail;
1343 goto bail;
1344 }
1344 }
1345
1345
1346 /* If the delta chain contains a single element, we do not need slicing
1346 /* If the delta chain contains a single element, we do not need slicing
1347 */
1347 */
1348 num_revs = PyList_GET_SIZE(list_revs);
1348 num_revs = PyList_GET_SIZE(list_revs);
1349 if (num_revs <= 1) {
1349 if (num_revs <= 1) {
1350 result = PyTuple_Pack(1, list_revs);
1350 result = PyTuple_Pack(1, list_revs);
1351 goto done;
1351 goto done;
1352 }
1352 }
1353
1353
1354 /* Turn the python list into a native integer array (for efficiency) */
1354 /* Turn the python list into a native integer array (for efficiency) */
1355 revs = (Py_ssize_t *)calloc(num_revs, sizeof(Py_ssize_t));
1355 revs = (Py_ssize_t *)calloc(num_revs, sizeof(Py_ssize_t));
1356 if (revs == NULL) {
1356 if (revs == NULL) {
1357 PyErr_NoMemory();
1357 PyErr_NoMemory();
1358 goto bail;
1358 goto bail;
1359 }
1359 }
1360 for (i = 0; i < num_revs; i++) {
1360 for (i = 0; i < num_revs; i++) {
1361 Py_ssize_t revnum = PyInt_AsLong(PyList_GET_ITEM(list_revs, i));
1361 Py_ssize_t revnum = PyInt_AsLong(PyList_GET_ITEM(list_revs, i));
1362 if (revnum == -1 && PyErr_Occurred()) {
1362 if (revnum == -1 && PyErr_Occurred()) {
1363 goto bail;
1363 goto bail;
1364 }
1364 }
1365 if (revnum < nullrev || revnum >= idxlen) {
1365 if (revnum < nullrev || revnum >= idxlen) {
1366 PyErr_Format(PyExc_IndexError,
1366 PyErr_Format(PyExc_IndexError,
1367 "index out of range: %zd", revnum);
1367 "index out of range: %zd", revnum);
1368 goto bail;
1368 goto bail;
1369 }
1369 }
1370 revs[i] = revnum;
1370 revs[i] = revnum;
1371 }
1371 }
1372
1372
1373 /* Compute and check various property of the unsliced delta chain */
1373 /* Compute and check various property of the unsliced delta chain */
1374 deltachainspan = index_segment_span(self, revs[0], revs[num_revs - 1]);
1374 deltachainspan = index_segment_span(self, revs[0], revs[num_revs - 1]);
1375 if (deltachainspan < 0) {
1375 if (deltachainspan < 0) {
1376 goto bail;
1376 goto bail;
1377 }
1377 }
1378
1378
1379 if (deltachainspan <= mingapsize) {
1379 if (deltachainspan <= mingapsize) {
1380 result = PyTuple_Pack(1, list_revs);
1380 result = PyTuple_Pack(1, list_revs);
1381 goto done;
1381 goto done;
1382 }
1382 }
1383 chainpayload = 0;
1383 chainpayload = 0;
1384 for (i = 0; i < num_revs; i++) {
1384 for (i = 0; i < num_revs; i++) {
1385 int tmp = index_get_length(self, revs[i]);
1385 int tmp = index_get_length(self, revs[i]);
1386 if (tmp < 0) {
1386 if (tmp < 0) {
1387 goto bail;
1387 goto bail;
1388 }
1388 }
1389 chainpayload += tmp;
1389 chainpayload += tmp;
1390 }
1390 }
1391
1391
1392 readdata = deltachainspan;
1392 readdata = deltachainspan;
1393 density = 1.0;
1393 density = 1.0;
1394
1394
1395 if (0 < deltachainspan) {
1395 if (0 < deltachainspan) {
1396 density = (double)chainpayload / (double)deltachainspan;
1396 density = (double)chainpayload / (double)deltachainspan;
1397 }
1397 }
1398
1398
1399 if (density >= targetdensity) {
1399 if (density >= targetdensity) {
1400 result = PyTuple_Pack(1, list_revs);
1400 result = PyTuple_Pack(1, list_revs);
1401 goto done;
1401 goto done;
1402 }
1402 }
1403
1403
1404 /* if chain is too sparse, look for relevant gaps */
1404 /* if chain is too sparse, look for relevant gaps */
1405 gaps = (struct Gap *)calloc(num_revs, sizeof(struct Gap));
1405 gaps = (struct Gap *)calloc(num_revs, sizeof(struct Gap));
1406 if (gaps == NULL) {
1406 if (gaps == NULL) {
1407 PyErr_NoMemory();
1407 PyErr_NoMemory();
1408 goto bail;
1408 goto bail;
1409 }
1409 }
1410
1410
1411 previous_end = -1;
1411 previous_end = -1;
1412 for (i = 0; i < num_revs; i++) {
1412 for (i = 0; i < num_revs; i++) {
1413 int64_t revstart;
1413 int64_t revstart;
1414 int revsize;
1414 int revsize;
1415 revstart = index_get_start(self, revs[i]);
1415 revstart = index_get_start(self, revs[i]);
1416 if (revstart < 0) {
1416 if (revstart < 0) {
1417 goto bail;
1417 goto bail;
1418 };
1418 };
1419 revsize = index_get_length(self, revs[i]);
1419 revsize = index_get_length(self, revs[i]);
1420 if (revsize < 0) {
1420 if (revsize < 0) {
1421 goto bail;
1421 goto bail;
1422 };
1422 };
1423 if (revsize == 0) {
1423 if (revsize == 0) {
1424 continue;
1424 continue;
1425 }
1425 }
1426 if (previous_end >= 0) {
1426 if (previous_end >= 0) {
1427 int64_t gapsize = revstart - previous_end;
1427 int64_t gapsize = revstart - previous_end;
1428 if (gapsize > mingapsize) {
1428 if (gapsize > mingapsize) {
1429 gaps[num_gaps].size = gapsize;
1429 gaps[num_gaps].size = gapsize;
1430 gaps[num_gaps].idx = i;
1430 gaps[num_gaps].idx = i;
1431 num_gaps += 1;
1431 num_gaps += 1;
1432 }
1432 }
1433 }
1433 }
1434 previous_end = revstart + revsize;
1434 previous_end = revstart + revsize;
1435 }
1435 }
1436 if (num_gaps == 0) {
1436 if (num_gaps == 0) {
1437 result = PyTuple_Pack(1, list_revs);
1437 result = PyTuple_Pack(1, list_revs);
1438 goto done;
1438 goto done;
1439 }
1439 }
1440 qsort(gaps, num_gaps, sizeof(struct Gap), &gap_compare);
1440 qsort(gaps, num_gaps, sizeof(struct Gap), &gap_compare);
1441
1441
1442 /* Slice the largest gap first, they improve the density the most */
1442 /* Slice the largest gap first, they improve the density the most */
1443 selected_indices =
1443 selected_indices =
1444 (Py_ssize_t *)malloc((num_gaps + 1) * sizeof(Py_ssize_t));
1444 (Py_ssize_t *)malloc((num_gaps + 1) * sizeof(Py_ssize_t));
1445 if (selected_indices == NULL) {
1445 if (selected_indices == NULL) {
1446 PyErr_NoMemory();
1446 PyErr_NoMemory();
1447 goto bail;
1447 goto bail;
1448 }
1448 }
1449
1449
1450 for (i = num_gaps - 1; i >= 0; i--) {
1450 for (i = num_gaps - 1; i >= 0; i--) {
1451 selected_indices[num_selected] = gaps[i].idx;
1451 selected_indices[num_selected] = gaps[i].idx;
1452 readdata -= gaps[i].size;
1452 readdata -= gaps[i].size;
1453 num_selected += 1;
1453 num_selected += 1;
1454 if (readdata <= 0) {
1454 if (readdata <= 0) {
1455 density = 1.0;
1455 density = 1.0;
1456 } else {
1456 } else {
1457 density = (double)chainpayload / (double)readdata;
1457 density = (double)chainpayload / (double)readdata;
1458 }
1458 }
1459 if (density >= targetdensity) {
1459 if (density >= targetdensity) {
1460 break;
1460 break;
1461 }
1461 }
1462 }
1462 }
1463 qsort(selected_indices, num_selected, sizeof(Py_ssize_t),
1463 qsort(selected_indices, num_selected, sizeof(Py_ssize_t),
1464 &Py_ssize_t_compare);
1464 &Py_ssize_t_compare);
1465
1465
1466 /* create the resulting slice */
1466 /* create the resulting slice */
1467 allchunks = PyList_New(0);
1467 allchunks = PyList_New(0);
1468 if (allchunks == NULL) {
1468 if (allchunks == NULL) {
1469 goto bail;
1469 goto bail;
1470 }
1470 }
1471 previdx = 0;
1471 previdx = 0;
1472 selected_indices[num_selected] = num_revs;
1472 selected_indices[num_selected] = num_revs;
1473 for (i = 0; i <= num_selected; i++) {
1473 for (i = 0; i <= num_selected; i++) {
1474 Py_ssize_t idx = selected_indices[i];
1474 Py_ssize_t idx = selected_indices[i];
1475 Py_ssize_t endidx = trim_endidx(self, revs, previdx, idx);
1475 Py_ssize_t endidx = trim_endidx(self, revs, previdx, idx);
1476 if (endidx < 0) {
1476 if (endidx < 0) {
1477 goto bail;
1477 goto bail;
1478 }
1478 }
1479 if (previdx < endidx) {
1479 if (previdx < endidx) {
1480 chunk = PyList_GetSlice(list_revs, previdx, endidx);
1480 chunk = PyList_GetSlice(list_revs, previdx, endidx);
1481 if (chunk == NULL) {
1481 if (chunk == NULL) {
1482 goto bail;
1482 goto bail;
1483 }
1483 }
1484 if (PyList_Append(allchunks, chunk) == -1) {
1484 if (PyList_Append(allchunks, chunk) == -1) {
1485 goto bail;
1485 goto bail;
1486 }
1486 }
1487 Py_DECREF(chunk);
1487 Py_DECREF(chunk);
1488 chunk = NULL;
1488 chunk = NULL;
1489 }
1489 }
1490 previdx = idx;
1490 previdx = idx;
1491 }
1491 }
1492 result = allchunks;
1492 result = allchunks;
1493 goto done;
1493 goto done;
1494
1494
1495 bail:
1495 bail:
1496 Py_XDECREF(allchunks);
1496 Py_XDECREF(allchunks);
1497 Py_XDECREF(chunk);
1497 Py_XDECREF(chunk);
1498 done:
1498 done:
1499 free(revs);
1499 free(revs);
1500 free(gaps);
1500 free(gaps);
1501 free(selected_indices);
1501 free(selected_indices);
1502 return result;
1502 return result;
1503 }
1503 }
1504
1504
1505 static inline int nt_level(const char *node, Py_ssize_t level)
1505 static inline int nt_level(const char *node, Py_ssize_t level)
1506 {
1506 {
1507 int v = node[level >> 1];
1507 int v = node[level >> 1];
1508 if (!(level & 1))
1508 if (!(level & 1))
1509 v >>= 4;
1509 v >>= 4;
1510 return v & 0xf;
1510 return v & 0xf;
1511 }
1511 }
1512
1512
1513 /*
1513 /*
1514 * Return values:
1514 * Return values:
1515 *
1515 *
1516 * -4: match is ambiguous (multiple candidates)
1516 * -4: match is ambiguous (multiple candidates)
1517 * -2: not found
1517 * -2: not found
1518 * rest: valid rev
1518 * rest: valid rev
1519 */
1519 */
1520 static int nt_find(nodetree *self, const char *node, Py_ssize_t nodelen,
1520 static int nt_find(nodetree *self, const char *node, Py_ssize_t nodelen,
1521 int hex)
1521 int hex)
1522 {
1522 {
1523 int (*getnybble)(const char *, Py_ssize_t) = hex ? hexdigit : nt_level;
1523 int (*getnybble)(const char *, Py_ssize_t) = hex ? hexdigit : nt_level;
1524 int level, maxlevel, off;
1524 int level, maxlevel, off;
1525
1525
1526 if (nodelen == 20 && node[0] == '\0' && memcmp(node, nullid, 20) == 0)
1526 if (nodelen == 20 && node[0] == '\0' && memcmp(node, nullid, 20) == 0)
1527 return -1;
1527 return -1;
1528
1528
1529 if (hex)
1529 if (hex)
1530 maxlevel = nodelen > 40 ? 40 : (int)nodelen;
1530 maxlevel = nodelen > 40 ? 40 : (int)nodelen;
1531 else
1531 else
1532 maxlevel = nodelen > 20 ? 40 : ((int)nodelen * 2);
1532 maxlevel = nodelen > 20 ? 40 : ((int)nodelen * 2);
1533
1533
1534 for (level = off = 0; level < maxlevel; level++) {
1534 for (level = off = 0; level < maxlevel; level++) {
1535 int k = getnybble(node, level);
1535 int k = getnybble(node, level);
1536 nodetreenode *n = &self->nodes[off];
1536 nodetreenode *n = &self->nodes[off];
1537 int v = n->children[k];
1537 int v = n->children[k];
1538
1538
1539 if (v < 0) {
1539 if (v < 0) {
1540 const char *n;
1540 const char *n;
1541 Py_ssize_t i;
1541 Py_ssize_t i;
1542
1542
1543 v = -(v + 2);
1543 v = -(v + 2);
1544 n = index_node(self->index, v);
1544 n = index_node(self->index, v);
1545 if (n == NULL)
1545 if (n == NULL)
1546 return -2;
1546 return -2;
1547 for (i = level; i < maxlevel; i++)
1547 for (i = level; i < maxlevel; i++)
1548 if (getnybble(node, i) != nt_level(n, i))
1548 if (getnybble(node, i) != nt_level(n, i))
1549 return -2;
1549 return -2;
1550 return v;
1550 return v;
1551 }
1551 }
1552 if (v == 0)
1552 if (v == 0)
1553 return -2;
1553 return -2;
1554 off = v;
1554 off = v;
1555 }
1555 }
1556 /* multiple matches against an ambiguous prefix */
1556 /* multiple matches against an ambiguous prefix */
1557 return -4;
1557 return -4;
1558 }
1558 }
1559
1559
1560 static int nt_new(nodetree *self)
1560 static int nt_new(nodetree *self)
1561 {
1561 {
1562 if (self->length == self->capacity) {
1562 if (self->length == self->capacity) {
1563 unsigned newcapacity;
1563 unsigned newcapacity;
1564 nodetreenode *newnodes;
1564 nodetreenode *newnodes;
1565 newcapacity = self->capacity * 2;
1565 newcapacity = self->capacity * 2;
1566 if (newcapacity >= INT_MAX / sizeof(nodetreenode)) {
1566 if (newcapacity >= INT_MAX / sizeof(nodetreenode)) {
1567 PyErr_SetString(PyExc_MemoryError,
1567 PyErr_SetString(PyExc_MemoryError,
1568 "overflow in nt_new");
1568 "overflow in nt_new");
1569 return -1;
1569 return -1;
1570 }
1570 }
1571 newnodes =
1571 newnodes =
1572 realloc(self->nodes, newcapacity * sizeof(nodetreenode));
1572 realloc(self->nodes, newcapacity * sizeof(nodetreenode));
1573 if (newnodes == NULL) {
1573 if (newnodes == NULL) {
1574 PyErr_SetString(PyExc_MemoryError, "out of memory");
1574 PyErr_SetString(PyExc_MemoryError, "out of memory");
1575 return -1;
1575 return -1;
1576 }
1576 }
1577 self->capacity = newcapacity;
1577 self->capacity = newcapacity;
1578 self->nodes = newnodes;
1578 self->nodes = newnodes;
1579 memset(&self->nodes[self->length], 0,
1579 memset(&self->nodes[self->length], 0,
1580 sizeof(nodetreenode) * (self->capacity - self->length));
1580 sizeof(nodetreenode) * (self->capacity - self->length));
1581 }
1581 }
1582 return self->length++;
1582 return self->length++;
1583 }
1583 }
1584
1584
1585 static int nt_insert(nodetree *self, const char *node, int rev)
1585 static int nt_insert(nodetree *self, const char *node, int rev)
1586 {
1586 {
1587 int level = 0;
1587 int level = 0;
1588 int off = 0;
1588 int off = 0;
1589
1589
1590 while (level < 40) {
1590 while (level < 40) {
1591 int k = nt_level(node, level);
1591 int k = nt_level(node, level);
1592 nodetreenode *n;
1592 nodetreenode *n;
1593 int v;
1593 int v;
1594
1594
1595 n = &self->nodes[off];
1595 n = &self->nodes[off];
1596 v = n->children[k];
1596 v = n->children[k];
1597
1597
1598 if (v == 0) {
1598 if (v == 0) {
1599 n->children[k] = -rev - 2;
1599 n->children[k] = -rev - 2;
1600 return 0;
1600 return 0;
1601 }
1601 }
1602 if (v < 0) {
1602 if (v < 0) {
1603 const char *oldnode =
1603 const char *oldnode =
1604 index_node_existing(self->index, -(v + 2));
1604 index_node_existing(self->index, -(v + 2));
1605 int noff;
1605 int noff;
1606
1606
1607 if (oldnode == NULL)
1607 if (oldnode == NULL)
1608 return -1;
1608 return -1;
1609 if (!memcmp(oldnode, node, 20)) {
1609 if (!memcmp(oldnode, node, 20)) {
1610 n->children[k] = -rev - 2;
1610 n->children[k] = -rev - 2;
1611 return 0;
1611 return 0;
1612 }
1612 }
1613 noff = nt_new(self);
1613 noff = nt_new(self);
1614 if (noff == -1)
1614 if (noff == -1)
1615 return -1;
1615 return -1;
1616 /* self->nodes may have been changed by realloc */
1616 /* self->nodes may have been changed by realloc */
1617 self->nodes[off].children[k] = noff;
1617 self->nodes[off].children[k] = noff;
1618 off = noff;
1618 off = noff;
1619 n = &self->nodes[off];
1619 n = &self->nodes[off];
1620 n->children[nt_level(oldnode, ++level)] = v;
1620 n->children[nt_level(oldnode, ++level)] = v;
1621 if (level > self->depth)
1621 if (level > self->depth)
1622 self->depth = level;
1622 self->depth = level;
1623 self->splits += 1;
1623 self->splits += 1;
1624 } else {
1624 } else {
1625 level += 1;
1625 level += 1;
1626 off = v;
1626 off = v;
1627 }
1627 }
1628 }
1628 }
1629
1629
1630 return -1;
1630 return -1;
1631 }
1631 }
1632
1632
1633 static PyObject *ntobj_insert(nodetreeObject *self, PyObject *args)
1633 static PyObject *ntobj_insert(nodetreeObject *self, PyObject *args)
1634 {
1634 {
1635 Py_ssize_t rev;
1635 Py_ssize_t rev;
1636 const char *node;
1636 const char *node;
1637 Py_ssize_t length;
1637 Py_ssize_t length;
1638 if (!PyArg_ParseTuple(args, "n", &rev))
1638 if (!PyArg_ParseTuple(args, "n", &rev))
1639 return NULL;
1639 return NULL;
1640 length = index_length(self->nt.index);
1640 length = index_length(self->nt.index);
1641 if (rev < 0 || rev >= length) {
1641 if (rev < 0 || rev >= length) {
1642 PyErr_SetString(PyExc_ValueError, "revlog index out of range");
1642 PyErr_SetString(PyExc_ValueError, "revlog index out of range");
1643 return NULL;
1643 return NULL;
1644 }
1644 }
1645 node = index_node_existing(self->nt.index, rev);
1645 node = index_node_existing(self->nt.index, rev);
1646 if (nt_insert(&self->nt, node, (int)rev) == -1)
1646 if (nt_insert(&self->nt, node, (int)rev) == -1)
1647 return NULL;
1647 return NULL;
1648 Py_RETURN_NONE;
1648 Py_RETURN_NONE;
1649 }
1649 }
1650
1650
1651 static int nt_delete_node(nodetree *self, const char *node)
1651 static int nt_delete_node(nodetree *self, const char *node)
1652 {
1652 {
1653 /* rev==-2 happens to get encoded as 0, which is interpreted as not set
1653 /* rev==-2 happens to get encoded as 0, which is interpreted as not set
1654 */
1654 */
1655 return nt_insert(self, node, -2);
1655 return nt_insert(self, node, -2);
1656 }
1656 }
1657
1657
1658 static int nt_init(nodetree *self, indexObject *index, unsigned capacity)
1658 static int nt_init(nodetree *self, indexObject *index, unsigned capacity)
1659 {
1659 {
1660 /* Initialize before overflow-checking to avoid nt_dealloc() crash. */
1660 /* Initialize before overflow-checking to avoid nt_dealloc() crash. */
1661 self->nodes = NULL;
1661 self->nodes = NULL;
1662
1662
1663 self->index = index;
1663 self->index = index;
1664 /* The input capacity is in terms of revisions, while the field is in
1664 /* The input capacity is in terms of revisions, while the field is in
1665 * terms of nodetree nodes. */
1665 * terms of nodetree nodes. */
1666 self->capacity = (capacity < 4 ? 4 : capacity / 2);
1666 self->capacity = (capacity < 4 ? 4 : capacity / 2);
1667 self->depth = 0;
1667 self->depth = 0;
1668 self->splits = 0;
1668 self->splits = 0;
1669 if ((size_t)self->capacity > INT_MAX / sizeof(nodetreenode)) {
1669 if ((size_t)self->capacity > INT_MAX / sizeof(nodetreenode)) {
1670 PyErr_SetString(PyExc_ValueError, "overflow in init_nt");
1670 PyErr_SetString(PyExc_ValueError, "overflow in init_nt");
1671 return -1;
1671 return -1;
1672 }
1672 }
1673 self->nodes = calloc(self->capacity, sizeof(nodetreenode));
1673 self->nodes = calloc(self->capacity, sizeof(nodetreenode));
1674 if (self->nodes == NULL) {
1674 if (self->nodes == NULL) {
1675 PyErr_NoMemory();
1675 PyErr_NoMemory();
1676 return -1;
1676 return -1;
1677 }
1677 }
1678 self->length = 1;
1678 self->length = 1;
1679 return 0;
1679 return 0;
1680 }
1680 }
1681
1681
1682 static int ntobj_init(nodetreeObject *self, PyObject *args)
1682 static int ntobj_init(nodetreeObject *self, PyObject *args)
1683 {
1683 {
1684 PyObject *index;
1684 PyObject *index;
1685 unsigned capacity;
1685 unsigned capacity;
1686 if (!PyArg_ParseTuple(args, "O!I", &HgRevlogIndex_Type, &index,
1686 if (!PyArg_ParseTuple(args, "O!I", &HgRevlogIndex_Type, &index,
1687 &capacity))
1687 &capacity))
1688 return -1;
1688 return -1;
1689 Py_INCREF(index);
1689 Py_INCREF(index);
1690 return nt_init(&self->nt, (indexObject *)index, capacity);
1690 return nt_init(&self->nt, (indexObject *)index, capacity);
1691 }
1691 }
1692
1692
1693 static int nt_partialmatch(nodetree *self, const char *node, Py_ssize_t nodelen)
1693 static int nt_partialmatch(nodetree *self, const char *node, Py_ssize_t nodelen)
1694 {
1694 {
1695 return nt_find(self, node, nodelen, 1);
1695 return nt_find(self, node, nodelen, 1);
1696 }
1696 }
1697
1697
1698 /*
1698 /*
1699 * Find the length of the shortest unique prefix of node.
1699 * Find the length of the shortest unique prefix of node.
1700 *
1700 *
1701 * Return values:
1701 * Return values:
1702 *
1702 *
1703 * -3: error (exception set)
1703 * -3: error (exception set)
1704 * -2: not found (no exception set)
1704 * -2: not found (no exception set)
1705 * rest: length of shortest prefix
1705 * rest: length of shortest prefix
1706 */
1706 */
1707 static int nt_shortest(nodetree *self, const char *node)
1707 static int nt_shortest(nodetree *self, const char *node)
1708 {
1708 {
1709 int level, off;
1709 int level, off;
1710
1710
1711 for (level = off = 0; level < 40; level++) {
1711 for (level = off = 0; level < 40; level++) {
1712 int k, v;
1712 int k, v;
1713 nodetreenode *n = &self->nodes[off];
1713 nodetreenode *n = &self->nodes[off];
1714 k = nt_level(node, level);
1714 k = nt_level(node, level);
1715 v = n->children[k];
1715 v = n->children[k];
1716 if (v < 0) {
1716 if (v < 0) {
1717 const char *n;
1717 const char *n;
1718 v = -(v + 2);
1718 v = -(v + 2);
1719 n = index_node_existing(self->index, v);
1719 n = index_node_existing(self->index, v);
1720 if (n == NULL)
1720 if (n == NULL)
1721 return -3;
1721 return -3;
1722 if (memcmp(node, n, 20) != 0)
1722 if (memcmp(node, n, 20) != 0)
1723 /*
1723 /*
1724 * Found a unique prefix, but it wasn't for the
1724 * Found a unique prefix, but it wasn't for the
1725 * requested node (i.e the requested node does
1725 * requested node (i.e the requested node does
1726 * not exist).
1726 * not exist).
1727 */
1727 */
1728 return -2;
1728 return -2;
1729 return level + 1;
1729 return level + 1;
1730 }
1730 }
1731 if (v == 0)
1731 if (v == 0)
1732 return -2;
1732 return -2;
1733 off = v;
1733 off = v;
1734 }
1734 }
1735 /*
1735 /*
1736 * The node was still not unique after 40 hex digits, so this won't
1736 * The node was still not unique after 40 hex digits, so this won't
1737 * happen. Also, if we get here, then there's a programming error in
1737 * happen. Also, if we get here, then there's a programming error in
1738 * this file that made us insert a node longer than 40 hex digits.
1738 * this file that made us insert a node longer than 40 hex digits.
1739 */
1739 */
1740 PyErr_SetString(PyExc_Exception, "broken node tree");
1740 PyErr_SetString(PyExc_Exception, "broken node tree");
1741 return -3;
1741 return -3;
1742 }
1742 }
1743
1743
1744 static PyObject *ntobj_shortest(nodetreeObject *self, PyObject *args)
1744 static PyObject *ntobj_shortest(nodetreeObject *self, PyObject *args)
1745 {
1745 {
1746 PyObject *val;
1746 PyObject *val;
1747 char *node;
1747 char *node;
1748 int length;
1748 int length;
1749
1749
1750 if (!PyArg_ParseTuple(args, "O", &val))
1750 if (!PyArg_ParseTuple(args, "O", &val))
1751 return NULL;
1751 return NULL;
1752 if (node_check(val, &node) == -1)
1752 if (node_check(val, &node) == -1)
1753 return NULL;
1753 return NULL;
1754
1754
1755 length = nt_shortest(&self->nt, node);
1755 length = nt_shortest(&self->nt, node);
1756 if (length == -3)
1756 if (length == -3)
1757 return NULL;
1757 return NULL;
1758 if (length == -2) {
1758 if (length == -2) {
1759 raise_revlog_error();
1759 raise_revlog_error();
1760 return NULL;
1760 return NULL;
1761 }
1761 }
1762 return PyInt_FromLong(length);
1762 return PyInt_FromLong(length);
1763 }
1763 }
1764
1764
1765 static void nt_dealloc(nodetree *self)
1765 static void nt_dealloc(nodetree *self)
1766 {
1766 {
1767 free(self->nodes);
1767 free(self->nodes);
1768 self->nodes = NULL;
1768 self->nodes = NULL;
1769 }
1769 }
1770
1770
1771 static void ntobj_dealloc(nodetreeObject *self)
1771 static void ntobj_dealloc(nodetreeObject *self)
1772 {
1772 {
1773 Py_XDECREF(self->nt.index);
1773 Py_XDECREF(self->nt.index);
1774 nt_dealloc(&self->nt);
1774 nt_dealloc(&self->nt);
1775 PyObject_Del(self);
1775 PyObject_Del(self);
1776 }
1776 }
1777
1777
1778 static PyMethodDef ntobj_methods[] = {
1778 static PyMethodDef ntobj_methods[] = {
1779 {"insert", (PyCFunction)ntobj_insert, METH_VARARGS,
1779 {"insert", (PyCFunction)ntobj_insert, METH_VARARGS,
1780 "insert an index entry"},
1780 "insert an index entry"},
1781 {"shortest", (PyCFunction)ntobj_shortest, METH_VARARGS,
1781 {"shortest", (PyCFunction)ntobj_shortest, METH_VARARGS,
1782 "find length of shortest hex nodeid of a binary ID"},
1782 "find length of shortest hex nodeid of a binary ID"},
1783 {NULL} /* Sentinel */
1783 {NULL} /* Sentinel */
1784 };
1784 };
1785
1785
1786 static PyTypeObject nodetreeType = {
1786 static PyTypeObject nodetreeType = {
1787 PyVarObject_HEAD_INIT(NULL, 0) /* header */
1787 PyVarObject_HEAD_INIT(NULL, 0) /* header */
1788 "parsers.nodetree", /* tp_name */
1788 "parsers.nodetree", /* tp_name */
1789 sizeof(nodetreeObject), /* tp_basicsize */
1789 sizeof(nodetreeObject), /* tp_basicsize */
1790 0, /* tp_itemsize */
1790 0, /* tp_itemsize */
1791 (destructor)ntobj_dealloc, /* tp_dealloc */
1791 (destructor)ntobj_dealloc, /* tp_dealloc */
1792 0, /* tp_print */
1792 0, /* tp_print */
1793 0, /* tp_getattr */
1793 0, /* tp_getattr */
1794 0, /* tp_setattr */
1794 0, /* tp_setattr */
1795 0, /* tp_compare */
1795 0, /* tp_compare */
1796 0, /* tp_repr */
1796 0, /* tp_repr */
1797 0, /* tp_as_number */
1797 0, /* tp_as_number */
1798 0, /* tp_as_sequence */
1798 0, /* tp_as_sequence */
1799 0, /* tp_as_mapping */
1799 0, /* tp_as_mapping */
1800 0, /* tp_hash */
1800 0, /* tp_hash */
1801 0, /* tp_call */
1801 0, /* tp_call */
1802 0, /* tp_str */
1802 0, /* tp_str */
1803 0, /* tp_getattro */
1803 0, /* tp_getattro */
1804 0, /* tp_setattro */
1804 0, /* tp_setattro */
1805 0, /* tp_as_buffer */
1805 0, /* tp_as_buffer */
1806 Py_TPFLAGS_DEFAULT, /* tp_flags */
1806 Py_TPFLAGS_DEFAULT, /* tp_flags */
1807 "nodetree", /* tp_doc */
1807 "nodetree", /* tp_doc */
1808 0, /* tp_traverse */
1808 0, /* tp_traverse */
1809 0, /* tp_clear */
1809 0, /* tp_clear */
1810 0, /* tp_richcompare */
1810 0, /* tp_richcompare */
1811 0, /* tp_weaklistoffset */
1811 0, /* tp_weaklistoffset */
1812 0, /* tp_iter */
1812 0, /* tp_iter */
1813 0, /* tp_iternext */
1813 0, /* tp_iternext */
1814 ntobj_methods, /* tp_methods */
1814 ntobj_methods, /* tp_methods */
1815 0, /* tp_members */
1815 0, /* tp_members */
1816 0, /* tp_getset */
1816 0, /* tp_getset */
1817 0, /* tp_base */
1817 0, /* tp_base */
1818 0, /* tp_dict */
1818 0, /* tp_dict */
1819 0, /* tp_descr_get */
1819 0, /* tp_descr_get */
1820 0, /* tp_descr_set */
1820 0, /* tp_descr_set */
1821 0, /* tp_dictoffset */
1821 0, /* tp_dictoffset */
1822 (initproc)ntobj_init, /* tp_init */
1822 (initproc)ntobj_init, /* tp_init */
1823 0, /* tp_alloc */
1823 0, /* tp_alloc */
1824 };
1824 };
1825
1825
1826 static int index_init_nt(indexObject *self)
1826 static int index_init_nt(indexObject *self)
1827 {
1827 {
1828 if (!self->ntinitialized) {
1828 if (!self->ntinitialized) {
1829 if (nt_init(&self->nt, self, (int)self->raw_length) == -1) {
1829 if (nt_init(&self->nt, self, (int)self->raw_length) == -1) {
1830 nt_dealloc(&self->nt);
1830 nt_dealloc(&self->nt);
1831 return -1;
1831 return -1;
1832 }
1832 }
1833 if (nt_insert(&self->nt, nullid, -1) == -1) {
1833 if (nt_insert(&self->nt, nullid, -1) == -1) {
1834 nt_dealloc(&self->nt);
1834 nt_dealloc(&self->nt);
1835 return -1;
1835 return -1;
1836 }
1836 }
1837 self->ntinitialized = 1;
1837 self->ntinitialized = 1;
1838 self->ntrev = (int)index_length(self);
1838 self->ntrev = (int)index_length(self);
1839 self->ntlookups = 1;
1839 self->ntlookups = 1;
1840 self->ntmisses = 0;
1840 self->ntmisses = 0;
1841 }
1841 }
1842 return 0;
1842 return 0;
1843 }
1843 }
1844
1844
1845 /*
1845 /*
1846 * Return values:
1846 * Return values:
1847 *
1847 *
1848 * -3: error (exception set)
1848 * -3: error (exception set)
1849 * -2: not found (no exception set)
1849 * -2: not found (no exception set)
1850 * rest: valid rev
1850 * rest: valid rev
1851 */
1851 */
1852 static int index_find_node(indexObject *self, const char *node,
1852 static int index_find_node(indexObject *self, const char *node,
1853 Py_ssize_t nodelen)
1853 Py_ssize_t nodelen)
1854 {
1854 {
1855 int rev;
1855 int rev;
1856
1856
1857 if (index_init_nt(self) == -1)
1857 if (index_init_nt(self) == -1)
1858 return -3;
1858 return -3;
1859
1859
1860 self->ntlookups++;
1860 self->ntlookups++;
1861 rev = nt_find(&self->nt, node, nodelen, 0);
1861 rev = nt_find(&self->nt, node, nodelen, 0);
1862 if (rev >= -1)
1862 if (rev >= -1)
1863 return rev;
1863 return rev;
1864
1864
1865 /*
1865 /*
1866 * For the first handful of lookups, we scan the entire index,
1866 * For the first handful of lookups, we scan the entire index,
1867 * and cache only the matching nodes. This optimizes for cases
1867 * and cache only the matching nodes. This optimizes for cases
1868 * like "hg tip", where only a few nodes are accessed.
1868 * like "hg tip", where only a few nodes are accessed.
1869 *
1869 *
1870 * After that, we cache every node we visit, using a single
1870 * After that, we cache every node we visit, using a single
1871 * scan amortized over multiple lookups. This gives the best
1871 * scan amortized over multiple lookups. This gives the best
1872 * bulk performance, e.g. for "hg log".
1872 * bulk performance, e.g. for "hg log".
1873 */
1873 */
1874 if (self->ntmisses++ < 4) {
1874 if (self->ntmisses++ < 4) {
1875 for (rev = self->ntrev - 1; rev >= 0; rev--) {
1875 for (rev = self->ntrev - 1; rev >= 0; rev--) {
1876 const char *n = index_node_existing(self, rev);
1876 const char *n = index_node_existing(self, rev);
1877 if (n == NULL)
1877 if (n == NULL)
1878 return -3;
1878 return -3;
1879 if (memcmp(node, n, nodelen > 20 ? 20 : nodelen) == 0) {
1879 if (memcmp(node, n, nodelen > 20 ? 20 : nodelen) == 0) {
1880 if (nt_insert(&self->nt, n, rev) == -1)
1880 if (nt_insert(&self->nt, n, rev) == -1)
1881 return -3;
1881 return -3;
1882 break;
1882 break;
1883 }
1883 }
1884 }
1884 }
1885 } else {
1885 } else {
1886 for (rev = self->ntrev - 1; rev >= 0; rev--) {
1886 for (rev = self->ntrev - 1; rev >= 0; rev--) {
1887 const char *n = index_node_existing(self, rev);
1887 const char *n = index_node_existing(self, rev);
1888 if (n == NULL)
1888 if (n == NULL)
1889 return -3;
1889 return -3;
1890 if (nt_insert(&self->nt, n, rev) == -1) {
1890 if (nt_insert(&self->nt, n, rev) == -1) {
1891 self->ntrev = rev + 1;
1891 self->ntrev = rev + 1;
1892 return -3;
1892 return -3;
1893 }
1893 }
1894 if (memcmp(node, n, nodelen > 20 ? 20 : nodelen) == 0) {
1894 if (memcmp(node, n, nodelen > 20 ? 20 : nodelen) == 0) {
1895 break;
1895 break;
1896 }
1896 }
1897 }
1897 }
1898 self->ntrev = rev;
1898 self->ntrev = rev;
1899 }
1899 }
1900
1900
1901 if (rev >= 0)
1901 if (rev >= 0)
1902 return rev;
1902 return rev;
1903 return -2;
1903 return -2;
1904 }
1904 }
1905
1905
1906 static PyObject *index_getitem(indexObject *self, PyObject *value)
1906 static PyObject *index_getitem(indexObject *self, PyObject *value)
1907 {
1907 {
1908 char *node;
1908 char *node;
1909 int rev;
1909 int rev;
1910
1910
1911 if (PyInt_Check(value)) {
1911 if (PyInt_Check(value)) {
1912 long idx;
1912 long idx;
1913 if (!pylong_to_long(value, &idx)) {
1913 if (!pylong_to_long(value, &idx)) {
1914 return NULL;
1914 return NULL;
1915 }
1915 }
1916 return index_get(self, idx);
1916 return index_get(self, idx);
1917 }
1917 }
1918
1918
1919 if (node_check(value, &node) == -1)
1919 if (node_check(value, &node) == -1)
1920 return NULL;
1920 return NULL;
1921 rev = index_find_node(self, node, 20);
1921 rev = index_find_node(self, node, 20);
1922 if (rev >= -1)
1922 if (rev >= -1)
1923 return PyInt_FromLong(rev);
1923 return PyInt_FromLong(rev);
1924 if (rev == -2)
1924 if (rev == -2)
1925 raise_revlog_error();
1925 raise_revlog_error();
1926 return NULL;
1926 return NULL;
1927 }
1927 }
1928
1928
1929 /*
1929 /*
1930 * Fully populate the radix tree.
1930 * Fully populate the radix tree.
1931 */
1931 */
1932 static int index_populate_nt(indexObject *self)
1932 static int index_populate_nt(indexObject *self)
1933 {
1933 {
1934 int rev;
1934 int rev;
1935 if (self->ntrev > 0) {
1935 if (self->ntrev > 0) {
1936 for (rev = self->ntrev - 1; rev >= 0; rev--) {
1936 for (rev = self->ntrev - 1; rev >= 0; rev--) {
1937 const char *n = index_node_existing(self, rev);
1937 const char *n = index_node_existing(self, rev);
1938 if (n == NULL)
1938 if (n == NULL)
1939 return -1;
1939 return -1;
1940 if (nt_insert(&self->nt, n, rev) == -1)
1940 if (nt_insert(&self->nt, n, rev) == -1)
1941 return -1;
1941 return -1;
1942 }
1942 }
1943 self->ntrev = -1;
1943 self->ntrev = -1;
1944 }
1944 }
1945 return 0;
1945 return 0;
1946 }
1946 }
1947
1947
1948 static PyObject *index_partialmatch(indexObject *self, PyObject *args)
1948 static PyObject *index_partialmatch(indexObject *self, PyObject *args)
1949 {
1949 {
1950 const char *fullnode;
1950 const char *fullnode;
1951 Py_ssize_t nodelen;
1951 Py_ssize_t nodelen;
1952 char *node;
1952 char *node;
1953 int rev, i;
1953 int rev, i;
1954
1954
1955 if (!PyArg_ParseTuple(args, PY23("s#", "y#"), &node, &nodelen))
1955 if (!PyArg_ParseTuple(args, PY23("s#", "y#"), &node, &nodelen))
1956 return NULL;
1956 return NULL;
1957
1957
1958 if (nodelen < 1) {
1958 if (nodelen < 1) {
1959 PyErr_SetString(PyExc_ValueError, "key too short");
1959 PyErr_SetString(PyExc_ValueError, "key too short");
1960 return NULL;
1960 return NULL;
1961 }
1961 }
1962
1962
1963 if (nodelen > 40) {
1963 if (nodelen > 40) {
1964 PyErr_SetString(PyExc_ValueError, "key too long");
1964 PyErr_SetString(PyExc_ValueError, "key too long");
1965 return NULL;
1965 return NULL;
1966 }
1966 }
1967
1967
1968 for (i = 0; i < nodelen; i++)
1968 for (i = 0; i < nodelen; i++)
1969 hexdigit(node, i);
1969 hexdigit(node, i);
1970 if (PyErr_Occurred()) {
1970 if (PyErr_Occurred()) {
1971 /* input contains non-hex characters */
1971 /* input contains non-hex characters */
1972 PyErr_Clear();
1972 PyErr_Clear();
1973 Py_RETURN_NONE;
1973 Py_RETURN_NONE;
1974 }
1974 }
1975
1975
1976 if (index_init_nt(self) == -1)
1976 if (index_init_nt(self) == -1)
1977 return NULL;
1977 return NULL;
1978 if (index_populate_nt(self) == -1)
1978 if (index_populate_nt(self) == -1)
1979 return NULL;
1979 return NULL;
1980 rev = nt_partialmatch(&self->nt, node, nodelen);
1980 rev = nt_partialmatch(&self->nt, node, nodelen);
1981
1981
1982 switch (rev) {
1982 switch (rev) {
1983 case -4:
1983 case -4:
1984 raise_revlog_error();
1984 raise_revlog_error();
1985 return NULL;
1985 return NULL;
1986 case -2:
1986 case -2:
1987 Py_RETURN_NONE;
1987 Py_RETURN_NONE;
1988 case -1:
1988 case -1:
1989 return PyBytes_FromStringAndSize(nullid, 20);
1989 return PyBytes_FromStringAndSize(nullid, 20);
1990 }
1990 }
1991
1991
1992 fullnode = index_node_existing(self, rev);
1992 fullnode = index_node_existing(self, rev);
1993 if (fullnode == NULL) {
1993 if (fullnode == NULL) {
1994 return NULL;
1994 return NULL;
1995 }
1995 }
1996 return PyBytes_FromStringAndSize(fullnode, 20);
1996 return PyBytes_FromStringAndSize(fullnode, 20);
1997 }
1997 }
1998
1998
1999 static PyObject *index_shortest(indexObject *self, PyObject *args)
1999 static PyObject *index_shortest(indexObject *self, PyObject *args)
2000 {
2000 {
2001 PyObject *val;
2001 PyObject *val;
2002 char *node;
2002 char *node;
2003 int length;
2003 int length;
2004
2004
2005 if (!PyArg_ParseTuple(args, "O", &val))
2005 if (!PyArg_ParseTuple(args, "O", &val))
2006 return NULL;
2006 return NULL;
2007 if (node_check(val, &node) == -1)
2007 if (node_check(val, &node) == -1)
2008 return NULL;
2008 return NULL;
2009
2009
2010 self->ntlookups++;
2010 self->ntlookups++;
2011 if (index_init_nt(self) == -1)
2011 if (index_init_nt(self) == -1)
2012 return NULL;
2012 return NULL;
2013 if (index_populate_nt(self) == -1)
2013 if (index_populate_nt(self) == -1)
2014 return NULL;
2014 return NULL;
2015 length = nt_shortest(&self->nt, node);
2015 length = nt_shortest(&self->nt, node);
2016 if (length == -3)
2016 if (length == -3)
2017 return NULL;
2017 return NULL;
2018 if (length == -2) {
2018 if (length == -2) {
2019 raise_revlog_error();
2019 raise_revlog_error();
2020 return NULL;
2020 return NULL;
2021 }
2021 }
2022 return PyInt_FromLong(length);
2022 return PyInt_FromLong(length);
2023 }
2023 }
2024
2024
2025 static PyObject *index_m_get(indexObject *self, PyObject *args)
2025 static PyObject *index_m_get(indexObject *self, PyObject *args)
2026 {
2026 {
2027 PyObject *val;
2027 PyObject *val;
2028 char *node;
2028 char *node;
2029 int rev;
2029 int rev;
2030
2030
2031 if (!PyArg_ParseTuple(args, "O", &val))
2031 if (!PyArg_ParseTuple(args, "O", &val))
2032 return NULL;
2032 return NULL;
2033 if (node_check(val, &node) == -1)
2033 if (node_check(val, &node) == -1)
2034 return NULL;
2034 return NULL;
2035 rev = index_find_node(self, node, 20);
2035 rev = index_find_node(self, node, 20);
2036 if (rev == -3)
2036 if (rev == -3)
2037 return NULL;
2037 return NULL;
2038 if (rev == -2)
2038 if (rev == -2)
2039 Py_RETURN_NONE;
2039 Py_RETURN_NONE;
2040 return PyInt_FromLong(rev);
2040 return PyInt_FromLong(rev);
2041 }
2041 }
2042
2042
2043 static int index_contains(indexObject *self, PyObject *value)
2043 static int index_contains(indexObject *self, PyObject *value)
2044 {
2044 {
2045 char *node;
2045 char *node;
2046
2046
2047 if (PyInt_Check(value)) {
2047 if (PyInt_Check(value)) {
2048 long rev;
2048 long rev;
2049 if (!pylong_to_long(value, &rev)) {
2049 if (!pylong_to_long(value, &rev)) {
2050 return -1;
2050 return -1;
2051 }
2051 }
2052 return rev >= -1 && rev < index_length(self);
2052 return rev >= -1 && rev < index_length(self);
2053 }
2053 }
2054
2054
2055 if (node_check(value, &node) == -1)
2055 if (node_check(value, &node) == -1)
2056 return -1;
2056 return -1;
2057
2057
2058 switch (index_find_node(self, node, 20)) {
2058 switch (index_find_node(self, node, 20)) {
2059 case -3:
2059 case -3:
2060 return -1;
2060 return -1;
2061 case -2:
2061 case -2:
2062 return 0;
2062 return 0;
2063 default:
2063 default:
2064 return 1;
2064 return 1;
2065 }
2065 }
2066 }
2066 }
2067
2067
2068 static PyObject *index_m_has_node(indexObject *self, PyObject *args)
2069 {
2070 int ret = index_contains(self, args);
2071 if (ret < 0)
2072 return NULL;
2073 return PyBool_FromLong((long)ret);
2074 }
2075
2068 typedef uint64_t bitmask;
2076 typedef uint64_t bitmask;
2069
2077
2070 /*
2078 /*
2071 * Given a disjoint set of revs, return all candidates for the
2079 * Given a disjoint set of revs, return all candidates for the
2072 * greatest common ancestor. In revset notation, this is the set
2080 * greatest common ancestor. In revset notation, this is the set
2073 * "heads(::a and ::b and ...)"
2081 * "heads(::a and ::b and ...)"
2074 */
2082 */
2075 static PyObject *find_gca_candidates(indexObject *self, const int *revs,
2083 static PyObject *find_gca_candidates(indexObject *self, const int *revs,
2076 int revcount)
2084 int revcount)
2077 {
2085 {
2078 const bitmask allseen = (1ull << revcount) - 1;
2086 const bitmask allseen = (1ull << revcount) - 1;
2079 const bitmask poison = 1ull << revcount;
2087 const bitmask poison = 1ull << revcount;
2080 PyObject *gca = PyList_New(0);
2088 PyObject *gca = PyList_New(0);
2081 int i, v, interesting;
2089 int i, v, interesting;
2082 int maxrev = -1;
2090 int maxrev = -1;
2083 bitmask sp;
2091 bitmask sp;
2084 bitmask *seen;
2092 bitmask *seen;
2085
2093
2086 if (gca == NULL)
2094 if (gca == NULL)
2087 return PyErr_NoMemory();
2095 return PyErr_NoMemory();
2088
2096
2089 for (i = 0; i < revcount; i++) {
2097 for (i = 0; i < revcount; i++) {
2090 if (revs[i] > maxrev)
2098 if (revs[i] > maxrev)
2091 maxrev = revs[i];
2099 maxrev = revs[i];
2092 }
2100 }
2093
2101
2094 seen = calloc(sizeof(*seen), maxrev + 1);
2102 seen = calloc(sizeof(*seen), maxrev + 1);
2095 if (seen == NULL) {
2103 if (seen == NULL) {
2096 Py_DECREF(gca);
2104 Py_DECREF(gca);
2097 return PyErr_NoMemory();
2105 return PyErr_NoMemory();
2098 }
2106 }
2099
2107
2100 for (i = 0; i < revcount; i++)
2108 for (i = 0; i < revcount; i++)
2101 seen[revs[i]] = 1ull << i;
2109 seen[revs[i]] = 1ull << i;
2102
2110
2103 interesting = revcount;
2111 interesting = revcount;
2104
2112
2105 for (v = maxrev; v >= 0 && interesting; v--) {
2113 for (v = maxrev; v >= 0 && interesting; v--) {
2106 bitmask sv = seen[v];
2114 bitmask sv = seen[v];
2107 int parents[2];
2115 int parents[2];
2108
2116
2109 if (!sv)
2117 if (!sv)
2110 continue;
2118 continue;
2111
2119
2112 if (sv < poison) {
2120 if (sv < poison) {
2113 interesting -= 1;
2121 interesting -= 1;
2114 if (sv == allseen) {
2122 if (sv == allseen) {
2115 PyObject *obj = PyInt_FromLong(v);
2123 PyObject *obj = PyInt_FromLong(v);
2116 if (obj == NULL)
2124 if (obj == NULL)
2117 goto bail;
2125 goto bail;
2118 if (PyList_Append(gca, obj) == -1) {
2126 if (PyList_Append(gca, obj) == -1) {
2119 Py_DECREF(obj);
2127 Py_DECREF(obj);
2120 goto bail;
2128 goto bail;
2121 }
2129 }
2122 sv |= poison;
2130 sv |= poison;
2123 for (i = 0; i < revcount; i++) {
2131 for (i = 0; i < revcount; i++) {
2124 if (revs[i] == v)
2132 if (revs[i] == v)
2125 goto done;
2133 goto done;
2126 }
2134 }
2127 }
2135 }
2128 }
2136 }
2129 if (index_get_parents(self, v, parents, maxrev) < 0)
2137 if (index_get_parents(self, v, parents, maxrev) < 0)
2130 goto bail;
2138 goto bail;
2131
2139
2132 for (i = 0; i < 2; i++) {
2140 for (i = 0; i < 2; i++) {
2133 int p = parents[i];
2141 int p = parents[i];
2134 if (p == -1)
2142 if (p == -1)
2135 continue;
2143 continue;
2136 sp = seen[p];
2144 sp = seen[p];
2137 if (sv < poison) {
2145 if (sv < poison) {
2138 if (sp == 0) {
2146 if (sp == 0) {
2139 seen[p] = sv;
2147 seen[p] = sv;
2140 interesting++;
2148 interesting++;
2141 } else if (sp != sv)
2149 } else if (sp != sv)
2142 seen[p] |= sv;
2150 seen[p] |= sv;
2143 } else {
2151 } else {
2144 if (sp && sp < poison)
2152 if (sp && sp < poison)
2145 interesting--;
2153 interesting--;
2146 seen[p] = sv;
2154 seen[p] = sv;
2147 }
2155 }
2148 }
2156 }
2149 }
2157 }
2150
2158
2151 done:
2159 done:
2152 free(seen);
2160 free(seen);
2153 return gca;
2161 return gca;
2154 bail:
2162 bail:
2155 free(seen);
2163 free(seen);
2156 Py_XDECREF(gca);
2164 Py_XDECREF(gca);
2157 return NULL;
2165 return NULL;
2158 }
2166 }
2159
2167
2160 /*
2168 /*
2161 * Given a disjoint set of revs, return the subset with the longest
2169 * Given a disjoint set of revs, return the subset with the longest
2162 * path to the root.
2170 * path to the root.
2163 */
2171 */
2164 static PyObject *find_deepest(indexObject *self, PyObject *revs)
2172 static PyObject *find_deepest(indexObject *self, PyObject *revs)
2165 {
2173 {
2166 const Py_ssize_t revcount = PyList_GET_SIZE(revs);
2174 const Py_ssize_t revcount = PyList_GET_SIZE(revs);
2167 static const Py_ssize_t capacity = 24;
2175 static const Py_ssize_t capacity = 24;
2168 int *depth, *interesting = NULL;
2176 int *depth, *interesting = NULL;
2169 int i, j, v, ninteresting;
2177 int i, j, v, ninteresting;
2170 PyObject *dict = NULL, *keys = NULL;
2178 PyObject *dict = NULL, *keys = NULL;
2171 long *seen = NULL;
2179 long *seen = NULL;
2172 int maxrev = -1;
2180 int maxrev = -1;
2173 long final;
2181 long final;
2174
2182
2175 if (revcount > capacity) {
2183 if (revcount > capacity) {
2176 PyErr_Format(PyExc_OverflowError,
2184 PyErr_Format(PyExc_OverflowError,
2177 "bitset size (%ld) > capacity (%ld)",
2185 "bitset size (%ld) > capacity (%ld)",
2178 (long)revcount, (long)capacity);
2186 (long)revcount, (long)capacity);
2179 return NULL;
2187 return NULL;
2180 }
2188 }
2181
2189
2182 for (i = 0; i < revcount; i++) {
2190 for (i = 0; i < revcount; i++) {
2183 int n = (int)PyInt_AsLong(PyList_GET_ITEM(revs, i));
2191 int n = (int)PyInt_AsLong(PyList_GET_ITEM(revs, i));
2184 if (n > maxrev)
2192 if (n > maxrev)
2185 maxrev = n;
2193 maxrev = n;
2186 }
2194 }
2187
2195
2188 depth = calloc(sizeof(*depth), maxrev + 1);
2196 depth = calloc(sizeof(*depth), maxrev + 1);
2189 if (depth == NULL)
2197 if (depth == NULL)
2190 return PyErr_NoMemory();
2198 return PyErr_NoMemory();
2191
2199
2192 seen = calloc(sizeof(*seen), maxrev + 1);
2200 seen = calloc(sizeof(*seen), maxrev + 1);
2193 if (seen == NULL) {
2201 if (seen == NULL) {
2194 PyErr_NoMemory();
2202 PyErr_NoMemory();
2195 goto bail;
2203 goto bail;
2196 }
2204 }
2197
2205
2198 interesting = calloc(sizeof(*interesting), ((size_t)1) << revcount);
2206 interesting = calloc(sizeof(*interesting), ((size_t)1) << revcount);
2199 if (interesting == NULL) {
2207 if (interesting == NULL) {
2200 PyErr_NoMemory();
2208 PyErr_NoMemory();
2201 goto bail;
2209 goto bail;
2202 }
2210 }
2203
2211
2204 if (PyList_Sort(revs) == -1)
2212 if (PyList_Sort(revs) == -1)
2205 goto bail;
2213 goto bail;
2206
2214
2207 for (i = 0; i < revcount; i++) {
2215 for (i = 0; i < revcount; i++) {
2208 int n = (int)PyInt_AsLong(PyList_GET_ITEM(revs, i));
2216 int n = (int)PyInt_AsLong(PyList_GET_ITEM(revs, i));
2209 long b = 1l << i;
2217 long b = 1l << i;
2210 depth[n] = 1;
2218 depth[n] = 1;
2211 seen[n] = b;
2219 seen[n] = b;
2212 interesting[b] = 1;
2220 interesting[b] = 1;
2213 }
2221 }
2214
2222
2215 /* invariant: ninteresting is the number of non-zero entries in
2223 /* invariant: ninteresting is the number of non-zero entries in
2216 * interesting. */
2224 * interesting. */
2217 ninteresting = (int)revcount;
2225 ninteresting = (int)revcount;
2218
2226
2219 for (v = maxrev; v >= 0 && ninteresting > 1; v--) {
2227 for (v = maxrev; v >= 0 && ninteresting > 1; v--) {
2220 int dv = depth[v];
2228 int dv = depth[v];
2221 int parents[2];
2229 int parents[2];
2222 long sv;
2230 long sv;
2223
2231
2224 if (dv == 0)
2232 if (dv == 0)
2225 continue;
2233 continue;
2226
2234
2227 sv = seen[v];
2235 sv = seen[v];
2228 if (index_get_parents(self, v, parents, maxrev) < 0)
2236 if (index_get_parents(self, v, parents, maxrev) < 0)
2229 goto bail;
2237 goto bail;
2230
2238
2231 for (i = 0; i < 2; i++) {
2239 for (i = 0; i < 2; i++) {
2232 int p = parents[i];
2240 int p = parents[i];
2233 long sp;
2241 long sp;
2234 int dp;
2242 int dp;
2235
2243
2236 if (p == -1)
2244 if (p == -1)
2237 continue;
2245 continue;
2238
2246
2239 dp = depth[p];
2247 dp = depth[p];
2240 sp = seen[p];
2248 sp = seen[p];
2241 if (dp <= dv) {
2249 if (dp <= dv) {
2242 depth[p] = dv + 1;
2250 depth[p] = dv + 1;
2243 if (sp != sv) {
2251 if (sp != sv) {
2244 interesting[sv] += 1;
2252 interesting[sv] += 1;
2245 seen[p] = sv;
2253 seen[p] = sv;
2246 if (sp) {
2254 if (sp) {
2247 interesting[sp] -= 1;
2255 interesting[sp] -= 1;
2248 if (interesting[sp] == 0)
2256 if (interesting[sp] == 0)
2249 ninteresting -= 1;
2257 ninteresting -= 1;
2250 }
2258 }
2251 }
2259 }
2252 } else if (dv == dp - 1) {
2260 } else if (dv == dp - 1) {
2253 long nsp = sp | sv;
2261 long nsp = sp | sv;
2254 if (nsp == sp)
2262 if (nsp == sp)
2255 continue;
2263 continue;
2256 seen[p] = nsp;
2264 seen[p] = nsp;
2257 interesting[sp] -= 1;
2265 interesting[sp] -= 1;
2258 if (interesting[sp] == 0)
2266 if (interesting[sp] == 0)
2259 ninteresting -= 1;
2267 ninteresting -= 1;
2260 if (interesting[nsp] == 0)
2268 if (interesting[nsp] == 0)
2261 ninteresting += 1;
2269 ninteresting += 1;
2262 interesting[nsp] += 1;
2270 interesting[nsp] += 1;
2263 }
2271 }
2264 }
2272 }
2265 interesting[sv] -= 1;
2273 interesting[sv] -= 1;
2266 if (interesting[sv] == 0)
2274 if (interesting[sv] == 0)
2267 ninteresting -= 1;
2275 ninteresting -= 1;
2268 }
2276 }
2269
2277
2270 final = 0;
2278 final = 0;
2271 j = ninteresting;
2279 j = ninteresting;
2272 for (i = 0; i < (int)(2 << revcount) && j > 0; i++) {
2280 for (i = 0; i < (int)(2 << revcount) && j > 0; i++) {
2273 if (interesting[i] == 0)
2281 if (interesting[i] == 0)
2274 continue;
2282 continue;
2275 final |= i;
2283 final |= i;
2276 j -= 1;
2284 j -= 1;
2277 }
2285 }
2278 if (final == 0) {
2286 if (final == 0) {
2279 keys = PyList_New(0);
2287 keys = PyList_New(0);
2280 goto bail;
2288 goto bail;
2281 }
2289 }
2282
2290
2283 dict = PyDict_New();
2291 dict = PyDict_New();
2284 if (dict == NULL)
2292 if (dict == NULL)
2285 goto bail;
2293 goto bail;
2286
2294
2287 for (i = 0; i < revcount; i++) {
2295 for (i = 0; i < revcount; i++) {
2288 PyObject *key;
2296 PyObject *key;
2289
2297
2290 if ((final & (1 << i)) == 0)
2298 if ((final & (1 << i)) == 0)
2291 continue;
2299 continue;
2292
2300
2293 key = PyList_GET_ITEM(revs, i);
2301 key = PyList_GET_ITEM(revs, i);
2294 Py_INCREF(key);
2302 Py_INCREF(key);
2295 Py_INCREF(Py_None);
2303 Py_INCREF(Py_None);
2296 if (PyDict_SetItem(dict, key, Py_None) == -1) {
2304 if (PyDict_SetItem(dict, key, Py_None) == -1) {
2297 Py_DECREF(key);
2305 Py_DECREF(key);
2298 Py_DECREF(Py_None);
2306 Py_DECREF(Py_None);
2299 goto bail;
2307 goto bail;
2300 }
2308 }
2301 }
2309 }
2302
2310
2303 keys = PyDict_Keys(dict);
2311 keys = PyDict_Keys(dict);
2304
2312
2305 bail:
2313 bail:
2306 free(depth);
2314 free(depth);
2307 free(seen);
2315 free(seen);
2308 free(interesting);
2316 free(interesting);
2309 Py_XDECREF(dict);
2317 Py_XDECREF(dict);
2310
2318
2311 return keys;
2319 return keys;
2312 }
2320 }
2313
2321
2314 /*
2322 /*
2315 * Given a (possibly overlapping) set of revs, return all the
2323 * Given a (possibly overlapping) set of revs, return all the
2316 * common ancestors heads: heads(::args[0] and ::a[1] and ...)
2324 * common ancestors heads: heads(::args[0] and ::a[1] and ...)
2317 */
2325 */
2318 static PyObject *index_commonancestorsheads(indexObject *self, PyObject *args)
2326 static PyObject *index_commonancestorsheads(indexObject *self, PyObject *args)
2319 {
2327 {
2320 PyObject *ret = NULL;
2328 PyObject *ret = NULL;
2321 Py_ssize_t argcount, i, len;
2329 Py_ssize_t argcount, i, len;
2322 bitmask repeat = 0;
2330 bitmask repeat = 0;
2323 int revcount = 0;
2331 int revcount = 0;
2324 int *revs;
2332 int *revs;
2325
2333
2326 argcount = PySequence_Length(args);
2334 argcount = PySequence_Length(args);
2327 revs = PyMem_Malloc(argcount * sizeof(*revs));
2335 revs = PyMem_Malloc(argcount * sizeof(*revs));
2328 if (argcount > 0 && revs == NULL)
2336 if (argcount > 0 && revs == NULL)
2329 return PyErr_NoMemory();
2337 return PyErr_NoMemory();
2330 len = index_length(self);
2338 len = index_length(self);
2331
2339
2332 for (i = 0; i < argcount; i++) {
2340 for (i = 0; i < argcount; i++) {
2333 static const int capacity = 24;
2341 static const int capacity = 24;
2334 PyObject *obj = PySequence_GetItem(args, i);
2342 PyObject *obj = PySequence_GetItem(args, i);
2335 bitmask x;
2343 bitmask x;
2336 long val;
2344 long val;
2337
2345
2338 if (!PyInt_Check(obj)) {
2346 if (!PyInt_Check(obj)) {
2339 PyErr_SetString(PyExc_TypeError,
2347 PyErr_SetString(PyExc_TypeError,
2340 "arguments must all be ints");
2348 "arguments must all be ints");
2341 Py_DECREF(obj);
2349 Py_DECREF(obj);
2342 goto bail;
2350 goto bail;
2343 }
2351 }
2344 val = PyInt_AsLong(obj);
2352 val = PyInt_AsLong(obj);
2345 Py_DECREF(obj);
2353 Py_DECREF(obj);
2346 if (val == -1) {
2354 if (val == -1) {
2347 ret = PyList_New(0);
2355 ret = PyList_New(0);
2348 goto done;
2356 goto done;
2349 }
2357 }
2350 if (val < 0 || val >= len) {
2358 if (val < 0 || val >= len) {
2351 PyErr_SetString(PyExc_IndexError, "index out of range");
2359 PyErr_SetString(PyExc_IndexError, "index out of range");
2352 goto bail;
2360 goto bail;
2353 }
2361 }
2354 /* this cheesy bloom filter lets us avoid some more
2362 /* this cheesy bloom filter lets us avoid some more
2355 * expensive duplicate checks in the common set-is-disjoint
2363 * expensive duplicate checks in the common set-is-disjoint
2356 * case */
2364 * case */
2357 x = 1ull << (val & 0x3f);
2365 x = 1ull << (val & 0x3f);
2358 if (repeat & x) {
2366 if (repeat & x) {
2359 int k;
2367 int k;
2360 for (k = 0; k < revcount; k++) {
2368 for (k = 0; k < revcount; k++) {
2361 if (val == revs[k])
2369 if (val == revs[k])
2362 goto duplicate;
2370 goto duplicate;
2363 }
2371 }
2364 } else
2372 } else
2365 repeat |= x;
2373 repeat |= x;
2366 if (revcount >= capacity) {
2374 if (revcount >= capacity) {
2367 PyErr_Format(PyExc_OverflowError,
2375 PyErr_Format(PyExc_OverflowError,
2368 "bitset size (%d) > capacity (%d)",
2376 "bitset size (%d) > capacity (%d)",
2369 revcount, capacity);
2377 revcount, capacity);
2370 goto bail;
2378 goto bail;
2371 }
2379 }
2372 revs[revcount++] = (int)val;
2380 revs[revcount++] = (int)val;
2373 duplicate:;
2381 duplicate:;
2374 }
2382 }
2375
2383
2376 if (revcount == 0) {
2384 if (revcount == 0) {
2377 ret = PyList_New(0);
2385 ret = PyList_New(0);
2378 goto done;
2386 goto done;
2379 }
2387 }
2380 if (revcount == 1) {
2388 if (revcount == 1) {
2381 PyObject *obj;
2389 PyObject *obj;
2382 ret = PyList_New(1);
2390 ret = PyList_New(1);
2383 if (ret == NULL)
2391 if (ret == NULL)
2384 goto bail;
2392 goto bail;
2385 obj = PyInt_FromLong(revs[0]);
2393 obj = PyInt_FromLong(revs[0]);
2386 if (obj == NULL)
2394 if (obj == NULL)
2387 goto bail;
2395 goto bail;
2388 PyList_SET_ITEM(ret, 0, obj);
2396 PyList_SET_ITEM(ret, 0, obj);
2389 goto done;
2397 goto done;
2390 }
2398 }
2391
2399
2392 ret = find_gca_candidates(self, revs, revcount);
2400 ret = find_gca_candidates(self, revs, revcount);
2393 if (ret == NULL)
2401 if (ret == NULL)
2394 goto bail;
2402 goto bail;
2395
2403
2396 done:
2404 done:
2397 PyMem_Free(revs);
2405 PyMem_Free(revs);
2398 return ret;
2406 return ret;
2399
2407
2400 bail:
2408 bail:
2401 PyMem_Free(revs);
2409 PyMem_Free(revs);
2402 Py_XDECREF(ret);
2410 Py_XDECREF(ret);
2403 return NULL;
2411 return NULL;
2404 }
2412 }
2405
2413
2406 /*
2414 /*
2407 * Given a (possibly overlapping) set of revs, return the greatest
2415 * Given a (possibly overlapping) set of revs, return the greatest
2408 * common ancestors: those with the longest path to the root.
2416 * common ancestors: those with the longest path to the root.
2409 */
2417 */
2410 static PyObject *index_ancestors(indexObject *self, PyObject *args)
2418 static PyObject *index_ancestors(indexObject *self, PyObject *args)
2411 {
2419 {
2412 PyObject *ret;
2420 PyObject *ret;
2413 PyObject *gca = index_commonancestorsheads(self, args);
2421 PyObject *gca = index_commonancestorsheads(self, args);
2414 if (gca == NULL)
2422 if (gca == NULL)
2415 return NULL;
2423 return NULL;
2416
2424
2417 if (PyList_GET_SIZE(gca) <= 1) {
2425 if (PyList_GET_SIZE(gca) <= 1) {
2418 return gca;
2426 return gca;
2419 }
2427 }
2420
2428
2421 ret = find_deepest(self, gca);
2429 ret = find_deepest(self, gca);
2422 Py_DECREF(gca);
2430 Py_DECREF(gca);
2423 return ret;
2431 return ret;
2424 }
2432 }
2425
2433
2426 /*
2434 /*
2427 * Invalidate any trie entries introduced by added revs.
2435 * Invalidate any trie entries introduced by added revs.
2428 */
2436 */
2429 static void index_invalidate_added(indexObject *self, Py_ssize_t start)
2437 static void index_invalidate_added(indexObject *self, Py_ssize_t start)
2430 {
2438 {
2431 Py_ssize_t i, len = PyList_GET_SIZE(self->added);
2439 Py_ssize_t i, len = PyList_GET_SIZE(self->added);
2432
2440
2433 for (i = start; i < len; i++) {
2441 for (i = start; i < len; i++) {
2434 PyObject *tuple = PyList_GET_ITEM(self->added, i);
2442 PyObject *tuple = PyList_GET_ITEM(self->added, i);
2435 PyObject *node = PyTuple_GET_ITEM(tuple, 7);
2443 PyObject *node = PyTuple_GET_ITEM(tuple, 7);
2436
2444
2437 nt_delete_node(&self->nt, PyBytes_AS_STRING(node));
2445 nt_delete_node(&self->nt, PyBytes_AS_STRING(node));
2438 }
2446 }
2439
2447
2440 if (start == 0)
2448 if (start == 0)
2441 Py_CLEAR(self->added);
2449 Py_CLEAR(self->added);
2442 }
2450 }
2443
2451
2444 /*
2452 /*
2445 * Delete a numeric range of revs, which must be at the end of the
2453 * Delete a numeric range of revs, which must be at the end of the
2446 * range, but exclude the sentinel nullid entry.
2454 * range, but exclude the sentinel nullid entry.
2447 */
2455 */
2448 static int index_slice_del(indexObject *self, PyObject *item)
2456 static int index_slice_del(indexObject *self, PyObject *item)
2449 {
2457 {
2450 Py_ssize_t start, stop, step, slicelength;
2458 Py_ssize_t start, stop, step, slicelength;
2451 Py_ssize_t length = index_length(self) + 1;
2459 Py_ssize_t length = index_length(self) + 1;
2452 int ret = 0;
2460 int ret = 0;
2453
2461
2454 /* Argument changed from PySliceObject* to PyObject* in Python 3. */
2462 /* Argument changed from PySliceObject* to PyObject* in Python 3. */
2455 #ifdef IS_PY3K
2463 #ifdef IS_PY3K
2456 if (PySlice_GetIndicesEx(item, length, &start, &stop, &step,
2464 if (PySlice_GetIndicesEx(item, length, &start, &stop, &step,
2457 &slicelength) < 0)
2465 &slicelength) < 0)
2458 #else
2466 #else
2459 if (PySlice_GetIndicesEx((PySliceObject *)item, length, &start, &stop,
2467 if (PySlice_GetIndicesEx((PySliceObject *)item, length, &start, &stop,
2460 &step, &slicelength) < 0)
2468 &step, &slicelength) < 0)
2461 #endif
2469 #endif
2462 return -1;
2470 return -1;
2463
2471
2464 if (slicelength <= 0)
2472 if (slicelength <= 0)
2465 return 0;
2473 return 0;
2466
2474
2467 if ((step < 0 && start < stop) || (step > 0 && start > stop))
2475 if ((step < 0 && start < stop) || (step > 0 && start > stop))
2468 stop = start;
2476 stop = start;
2469
2477
2470 if (step < 0) {
2478 if (step < 0) {
2471 stop = start + 1;
2479 stop = start + 1;
2472 start = stop + step * (slicelength - 1) - 1;
2480 start = stop + step * (slicelength - 1) - 1;
2473 step = -step;
2481 step = -step;
2474 }
2482 }
2475
2483
2476 if (step != 1) {
2484 if (step != 1) {
2477 PyErr_SetString(PyExc_ValueError,
2485 PyErr_SetString(PyExc_ValueError,
2478 "revlog index delete requires step size of 1");
2486 "revlog index delete requires step size of 1");
2479 return -1;
2487 return -1;
2480 }
2488 }
2481
2489
2482 if (stop != length - 1) {
2490 if (stop != length - 1) {
2483 PyErr_SetString(PyExc_IndexError,
2491 PyErr_SetString(PyExc_IndexError,
2484 "revlog index deletion indices are invalid");
2492 "revlog index deletion indices are invalid");
2485 return -1;
2493 return -1;
2486 }
2494 }
2487
2495
2488 if (start < self->length) {
2496 if (start < self->length) {
2489 if (self->ntinitialized) {
2497 if (self->ntinitialized) {
2490 Py_ssize_t i;
2498 Py_ssize_t i;
2491
2499
2492 for (i = start; i < self->length; i++) {
2500 for (i = start; i < self->length; i++) {
2493 const char *node = index_node_existing(self, i);
2501 const char *node = index_node_existing(self, i);
2494 if (node == NULL)
2502 if (node == NULL)
2495 return -1;
2503 return -1;
2496
2504
2497 nt_delete_node(&self->nt, node);
2505 nt_delete_node(&self->nt, node);
2498 }
2506 }
2499 if (self->added)
2507 if (self->added)
2500 index_invalidate_added(self, 0);
2508 index_invalidate_added(self, 0);
2501 if (self->ntrev > start)
2509 if (self->ntrev > start)
2502 self->ntrev = (int)start;
2510 self->ntrev = (int)start;
2503 }
2511 }
2504 self->length = start;
2512 self->length = start;
2505 if (start < self->raw_length) {
2513 if (start < self->raw_length) {
2506 if (self->cache) {
2514 if (self->cache) {
2507 Py_ssize_t i;
2515 Py_ssize_t i;
2508 for (i = start; i < self->raw_length; i++)
2516 for (i = start; i < self->raw_length; i++)
2509 Py_CLEAR(self->cache[i]);
2517 Py_CLEAR(self->cache[i]);
2510 }
2518 }
2511 self->raw_length = start;
2519 self->raw_length = start;
2512 }
2520 }
2513 goto done;
2521 goto done;
2514 }
2522 }
2515
2523
2516 if (self->ntinitialized) {
2524 if (self->ntinitialized) {
2517 index_invalidate_added(self, start - self->length);
2525 index_invalidate_added(self, start - self->length);
2518 if (self->ntrev > start)
2526 if (self->ntrev > start)
2519 self->ntrev = (int)start;
2527 self->ntrev = (int)start;
2520 }
2528 }
2521 if (self->added)
2529 if (self->added)
2522 ret = PyList_SetSlice(self->added, start - self->length,
2530 ret = PyList_SetSlice(self->added, start - self->length,
2523 PyList_GET_SIZE(self->added), NULL);
2531 PyList_GET_SIZE(self->added), NULL);
2524 done:
2532 done:
2525 Py_CLEAR(self->headrevs);
2533 Py_CLEAR(self->headrevs);
2526 return ret;
2534 return ret;
2527 }
2535 }
2528
2536
2529 /*
2537 /*
2530 * Supported ops:
2538 * Supported ops:
2531 *
2539 *
2532 * slice deletion
2540 * slice deletion
2533 * string assignment (extend node->rev mapping)
2541 * string assignment (extend node->rev mapping)
2534 * string deletion (shrink node->rev mapping)
2542 * string deletion (shrink node->rev mapping)
2535 */
2543 */
2536 static int index_assign_subscript(indexObject *self, PyObject *item,
2544 static int index_assign_subscript(indexObject *self, PyObject *item,
2537 PyObject *value)
2545 PyObject *value)
2538 {
2546 {
2539 char *node;
2547 char *node;
2540 long rev;
2548 long rev;
2541
2549
2542 if (PySlice_Check(item) && value == NULL)
2550 if (PySlice_Check(item) && value == NULL)
2543 return index_slice_del(self, item);
2551 return index_slice_del(self, item);
2544
2552
2545 if (node_check(item, &node) == -1)
2553 if (node_check(item, &node) == -1)
2546 return -1;
2554 return -1;
2547
2555
2548 if (value == NULL)
2556 if (value == NULL)
2549 return self->ntinitialized ? nt_delete_node(&self->nt, node)
2557 return self->ntinitialized ? nt_delete_node(&self->nt, node)
2550 : 0;
2558 : 0;
2551 rev = PyInt_AsLong(value);
2559 rev = PyInt_AsLong(value);
2552 if (rev > INT_MAX || rev < 0) {
2560 if (rev > INT_MAX || rev < 0) {
2553 if (!PyErr_Occurred())
2561 if (!PyErr_Occurred())
2554 PyErr_SetString(PyExc_ValueError, "rev out of range");
2562 PyErr_SetString(PyExc_ValueError, "rev out of range");
2555 return -1;
2563 return -1;
2556 }
2564 }
2557
2565
2558 if (index_init_nt(self) == -1)
2566 if (index_init_nt(self) == -1)
2559 return -1;
2567 return -1;
2560 return nt_insert(&self->nt, node, (int)rev);
2568 return nt_insert(&self->nt, node, (int)rev);
2561 }
2569 }
2562
2570
2563 /*
2571 /*
2564 * Find all RevlogNG entries in an index that has inline data. Update
2572 * Find all RevlogNG entries in an index that has inline data. Update
2565 * the optional "offsets" table with those entries.
2573 * the optional "offsets" table with those entries.
2566 */
2574 */
2567 static Py_ssize_t inline_scan(indexObject *self, const char **offsets)
2575 static Py_ssize_t inline_scan(indexObject *self, const char **offsets)
2568 {
2576 {
2569 const char *data = (const char *)self->buf.buf;
2577 const char *data = (const char *)self->buf.buf;
2570 Py_ssize_t pos = 0;
2578 Py_ssize_t pos = 0;
2571 Py_ssize_t end = self->buf.len;
2579 Py_ssize_t end = self->buf.len;
2572 long incr = v1_hdrsize;
2580 long incr = v1_hdrsize;
2573 Py_ssize_t len = 0;
2581 Py_ssize_t len = 0;
2574
2582
2575 while (pos + v1_hdrsize <= end && pos >= 0) {
2583 while (pos + v1_hdrsize <= end && pos >= 0) {
2576 uint32_t comp_len;
2584 uint32_t comp_len;
2577 /* 3rd element of header is length of compressed inline data */
2585 /* 3rd element of header is length of compressed inline data */
2578 comp_len = getbe32(data + pos + 8);
2586 comp_len = getbe32(data + pos + 8);
2579 incr = v1_hdrsize + comp_len;
2587 incr = v1_hdrsize + comp_len;
2580 if (offsets)
2588 if (offsets)
2581 offsets[len] = data + pos;
2589 offsets[len] = data + pos;
2582 len++;
2590 len++;
2583 pos += incr;
2591 pos += incr;
2584 }
2592 }
2585
2593
2586 if (pos != end) {
2594 if (pos != end) {
2587 if (!PyErr_Occurred())
2595 if (!PyErr_Occurred())
2588 PyErr_SetString(PyExc_ValueError, "corrupt index file");
2596 PyErr_SetString(PyExc_ValueError, "corrupt index file");
2589 return -1;
2597 return -1;
2590 }
2598 }
2591
2599
2592 return len;
2600 return len;
2593 }
2601 }
2594
2602
2595 static int index_init(indexObject *self, PyObject *args)
2603 static int index_init(indexObject *self, PyObject *args)
2596 {
2604 {
2597 PyObject *data_obj, *inlined_obj;
2605 PyObject *data_obj, *inlined_obj;
2598 Py_ssize_t size;
2606 Py_ssize_t size;
2599
2607
2600 /* Initialize before argument-checking to avoid index_dealloc() crash.
2608 /* Initialize before argument-checking to avoid index_dealloc() crash.
2601 */
2609 */
2602 self->raw_length = 0;
2610 self->raw_length = 0;
2603 self->added = NULL;
2611 self->added = NULL;
2604 self->cache = NULL;
2612 self->cache = NULL;
2605 self->data = NULL;
2613 self->data = NULL;
2606 memset(&self->buf, 0, sizeof(self->buf));
2614 memset(&self->buf, 0, sizeof(self->buf));
2607 self->headrevs = NULL;
2615 self->headrevs = NULL;
2608 self->filteredrevs = Py_None;
2616 self->filteredrevs = Py_None;
2609 Py_INCREF(Py_None);
2617 Py_INCREF(Py_None);
2610 self->ntinitialized = 0;
2618 self->ntinitialized = 0;
2611 self->offsets = NULL;
2619 self->offsets = NULL;
2612
2620
2613 if (!PyArg_ParseTuple(args, "OO", &data_obj, &inlined_obj))
2621 if (!PyArg_ParseTuple(args, "OO", &data_obj, &inlined_obj))
2614 return -1;
2622 return -1;
2615 if (!PyObject_CheckBuffer(data_obj)) {
2623 if (!PyObject_CheckBuffer(data_obj)) {
2616 PyErr_SetString(PyExc_TypeError,
2624 PyErr_SetString(PyExc_TypeError,
2617 "data does not support buffer interface");
2625 "data does not support buffer interface");
2618 return -1;
2626 return -1;
2619 }
2627 }
2620
2628
2621 if (PyObject_GetBuffer(data_obj, &self->buf, PyBUF_SIMPLE) == -1)
2629 if (PyObject_GetBuffer(data_obj, &self->buf, PyBUF_SIMPLE) == -1)
2622 return -1;
2630 return -1;
2623 size = self->buf.len;
2631 size = self->buf.len;
2624
2632
2625 self->inlined = inlined_obj && PyObject_IsTrue(inlined_obj);
2633 self->inlined = inlined_obj && PyObject_IsTrue(inlined_obj);
2626 self->data = data_obj;
2634 self->data = data_obj;
2627
2635
2628 self->ntlookups = self->ntmisses = 0;
2636 self->ntlookups = self->ntmisses = 0;
2629 self->ntrev = -1;
2637 self->ntrev = -1;
2630 Py_INCREF(self->data);
2638 Py_INCREF(self->data);
2631
2639
2632 if (self->inlined) {
2640 if (self->inlined) {
2633 Py_ssize_t len = inline_scan(self, NULL);
2641 Py_ssize_t len = inline_scan(self, NULL);
2634 if (len == -1)
2642 if (len == -1)
2635 goto bail;
2643 goto bail;
2636 self->raw_length = len;
2644 self->raw_length = len;
2637 self->length = len;
2645 self->length = len;
2638 } else {
2646 } else {
2639 if (size % v1_hdrsize) {
2647 if (size % v1_hdrsize) {
2640 PyErr_SetString(PyExc_ValueError, "corrupt index file");
2648 PyErr_SetString(PyExc_ValueError, "corrupt index file");
2641 goto bail;
2649 goto bail;
2642 }
2650 }
2643 self->raw_length = size / v1_hdrsize;
2651 self->raw_length = size / v1_hdrsize;
2644 self->length = self->raw_length;
2652 self->length = self->raw_length;
2645 }
2653 }
2646
2654
2647 return 0;
2655 return 0;
2648 bail:
2656 bail:
2649 return -1;
2657 return -1;
2650 }
2658 }
2651
2659
2652 static PyObject *index_nodemap(indexObject *self)
2660 static PyObject *index_nodemap(indexObject *self)
2653 {
2661 {
2654 Py_INCREF(self);
2662 Py_INCREF(self);
2655 return (PyObject *)self;
2663 return (PyObject *)self;
2656 }
2664 }
2657
2665
2658 static void _index_clearcaches(indexObject *self)
2666 static void _index_clearcaches(indexObject *self)
2659 {
2667 {
2660 if (self->cache) {
2668 if (self->cache) {
2661 Py_ssize_t i;
2669 Py_ssize_t i;
2662
2670
2663 for (i = 0; i < self->raw_length; i++)
2671 for (i = 0; i < self->raw_length; i++)
2664 Py_CLEAR(self->cache[i]);
2672 Py_CLEAR(self->cache[i]);
2665 free(self->cache);
2673 free(self->cache);
2666 self->cache = NULL;
2674 self->cache = NULL;
2667 }
2675 }
2668 if (self->offsets) {
2676 if (self->offsets) {
2669 PyMem_Free((void *)self->offsets);
2677 PyMem_Free((void *)self->offsets);
2670 self->offsets = NULL;
2678 self->offsets = NULL;
2671 }
2679 }
2672 if (self->ntinitialized) {
2680 if (self->ntinitialized) {
2673 nt_dealloc(&self->nt);
2681 nt_dealloc(&self->nt);
2674 }
2682 }
2675 self->ntinitialized = 0;
2683 self->ntinitialized = 0;
2676 Py_CLEAR(self->headrevs);
2684 Py_CLEAR(self->headrevs);
2677 }
2685 }
2678
2686
2679 static PyObject *index_clearcaches(indexObject *self)
2687 static PyObject *index_clearcaches(indexObject *self)
2680 {
2688 {
2681 _index_clearcaches(self);
2689 _index_clearcaches(self);
2682 self->ntrev = -1;
2690 self->ntrev = -1;
2683 self->ntlookups = self->ntmisses = 0;
2691 self->ntlookups = self->ntmisses = 0;
2684 Py_RETURN_NONE;
2692 Py_RETURN_NONE;
2685 }
2693 }
2686
2694
2687 static void index_dealloc(indexObject *self)
2695 static void index_dealloc(indexObject *self)
2688 {
2696 {
2689 _index_clearcaches(self);
2697 _index_clearcaches(self);
2690 Py_XDECREF(self->filteredrevs);
2698 Py_XDECREF(self->filteredrevs);
2691 if (self->buf.buf) {
2699 if (self->buf.buf) {
2692 PyBuffer_Release(&self->buf);
2700 PyBuffer_Release(&self->buf);
2693 memset(&self->buf, 0, sizeof(self->buf));
2701 memset(&self->buf, 0, sizeof(self->buf));
2694 }
2702 }
2695 Py_XDECREF(self->data);
2703 Py_XDECREF(self->data);
2696 Py_XDECREF(self->added);
2704 Py_XDECREF(self->added);
2697 PyObject_Del(self);
2705 PyObject_Del(self);
2698 }
2706 }
2699
2707
2700 static PySequenceMethods index_sequence_methods = {
2708 static PySequenceMethods index_sequence_methods = {
2701 (lenfunc)index_length, /* sq_length */
2709 (lenfunc)index_length, /* sq_length */
2702 0, /* sq_concat */
2710 0, /* sq_concat */
2703 0, /* sq_repeat */
2711 0, /* sq_repeat */
2704 (ssizeargfunc)index_get, /* sq_item */
2712 (ssizeargfunc)index_get, /* sq_item */
2705 0, /* sq_slice */
2713 0, /* sq_slice */
2706 0, /* sq_ass_item */
2714 0, /* sq_ass_item */
2707 0, /* sq_ass_slice */
2715 0, /* sq_ass_slice */
2708 (objobjproc)index_contains, /* sq_contains */
2716 (objobjproc)index_contains, /* sq_contains */
2709 };
2717 };
2710
2718
2711 static PyMappingMethods index_mapping_methods = {
2719 static PyMappingMethods index_mapping_methods = {
2712 (lenfunc)index_length, /* mp_length */
2720 (lenfunc)index_length, /* mp_length */
2713 (binaryfunc)index_getitem, /* mp_subscript */
2721 (binaryfunc)index_getitem, /* mp_subscript */
2714 (objobjargproc)index_assign_subscript, /* mp_ass_subscript */
2722 (objobjargproc)index_assign_subscript, /* mp_ass_subscript */
2715 };
2723 };
2716
2724
2717 static PyMethodDef index_methods[] = {
2725 static PyMethodDef index_methods[] = {
2718 {"ancestors", (PyCFunction)index_ancestors, METH_VARARGS,
2726 {"ancestors", (PyCFunction)index_ancestors, METH_VARARGS,
2719 "return the gca set of the given revs"},
2727 "return the gca set of the given revs"},
2720 {"commonancestorsheads", (PyCFunction)index_commonancestorsheads,
2728 {"commonancestorsheads", (PyCFunction)index_commonancestorsheads,
2721 METH_VARARGS,
2729 METH_VARARGS,
2722 "return the heads of the common ancestors of the given revs"},
2730 "return the heads of the common ancestors of the given revs"},
2723 {"clearcaches", (PyCFunction)index_clearcaches, METH_NOARGS,
2731 {"clearcaches", (PyCFunction)index_clearcaches, METH_NOARGS,
2724 "clear the index caches"},
2732 "clear the index caches"},
2725 {"get", (PyCFunction)index_m_get, METH_VARARGS, "get an index entry"},
2733 {"get", (PyCFunction)index_m_get, METH_VARARGS, "get an index entry"},
2734 {"has_node", (PyCFunction)index_m_has_node, METH_O,
2735 "return True if the node exist in the index"},
2726 {"computephasesmapsets", (PyCFunction)compute_phases_map_sets, METH_VARARGS,
2736 {"computephasesmapsets", (PyCFunction)compute_phases_map_sets, METH_VARARGS,
2727 "compute phases"},
2737 "compute phases"},
2728 {"reachableroots2", (PyCFunction)reachableroots2, METH_VARARGS,
2738 {"reachableroots2", (PyCFunction)reachableroots2, METH_VARARGS,
2729 "reachableroots"},
2739 "reachableroots"},
2730 {"headrevs", (PyCFunction)index_headrevs, METH_VARARGS,
2740 {"headrevs", (PyCFunction)index_headrevs, METH_VARARGS,
2731 "get head revisions"}, /* Can do filtering since 3.2 */
2741 "get head revisions"}, /* Can do filtering since 3.2 */
2732 {"headrevsfiltered", (PyCFunction)index_headrevs, METH_VARARGS,
2742 {"headrevsfiltered", (PyCFunction)index_headrevs, METH_VARARGS,
2733 "get filtered head revisions"}, /* Can always do filtering */
2743 "get filtered head revisions"}, /* Can always do filtering */
2734 {"issnapshot", (PyCFunction)index_issnapshot, METH_O,
2744 {"issnapshot", (PyCFunction)index_issnapshot, METH_O,
2735 "True if the object is a snapshot"},
2745 "True if the object is a snapshot"},
2736 {"findsnapshots", (PyCFunction)index_findsnapshots, METH_VARARGS,
2746 {"findsnapshots", (PyCFunction)index_findsnapshots, METH_VARARGS,
2737 "Gather snapshot data in a cache dict"},
2747 "Gather snapshot data in a cache dict"},
2738 {"deltachain", (PyCFunction)index_deltachain, METH_VARARGS,
2748 {"deltachain", (PyCFunction)index_deltachain, METH_VARARGS,
2739 "determine revisions with deltas to reconstruct fulltext"},
2749 "determine revisions with deltas to reconstruct fulltext"},
2740 {"slicechunktodensity", (PyCFunction)index_slicechunktodensity,
2750 {"slicechunktodensity", (PyCFunction)index_slicechunktodensity,
2741 METH_VARARGS, "determine revisions with deltas to reconstruct fulltext"},
2751 METH_VARARGS, "determine revisions with deltas to reconstruct fulltext"},
2742 {"append", (PyCFunction)index_append, METH_O, "append an index entry"},
2752 {"append", (PyCFunction)index_append, METH_O, "append an index entry"},
2743 {"partialmatch", (PyCFunction)index_partialmatch, METH_VARARGS,
2753 {"partialmatch", (PyCFunction)index_partialmatch, METH_VARARGS,
2744 "match a potentially ambiguous node ID"},
2754 "match a potentially ambiguous node ID"},
2745 {"shortest", (PyCFunction)index_shortest, METH_VARARGS,
2755 {"shortest", (PyCFunction)index_shortest, METH_VARARGS,
2746 "find length of shortest hex nodeid of a binary ID"},
2756 "find length of shortest hex nodeid of a binary ID"},
2747 {"stats", (PyCFunction)index_stats, METH_NOARGS, "stats for the index"},
2757 {"stats", (PyCFunction)index_stats, METH_NOARGS, "stats for the index"},
2748 {NULL} /* Sentinel */
2758 {NULL} /* Sentinel */
2749 };
2759 };
2750
2760
2751 static PyGetSetDef index_getset[] = {
2761 static PyGetSetDef index_getset[] = {
2752 {"nodemap", (getter)index_nodemap, NULL, "nodemap", NULL},
2762 {"nodemap", (getter)index_nodemap, NULL, "nodemap", NULL},
2753 {NULL} /* Sentinel */
2763 {NULL} /* Sentinel */
2754 };
2764 };
2755
2765
2756 PyTypeObject HgRevlogIndex_Type = {
2766 PyTypeObject HgRevlogIndex_Type = {
2757 PyVarObject_HEAD_INIT(NULL, 0) /* header */
2767 PyVarObject_HEAD_INIT(NULL, 0) /* header */
2758 "parsers.index", /* tp_name */
2768 "parsers.index", /* tp_name */
2759 sizeof(indexObject), /* tp_basicsize */
2769 sizeof(indexObject), /* tp_basicsize */
2760 0, /* tp_itemsize */
2770 0, /* tp_itemsize */
2761 (destructor)index_dealloc, /* tp_dealloc */
2771 (destructor)index_dealloc, /* tp_dealloc */
2762 0, /* tp_print */
2772 0, /* tp_print */
2763 0, /* tp_getattr */
2773 0, /* tp_getattr */
2764 0, /* tp_setattr */
2774 0, /* tp_setattr */
2765 0, /* tp_compare */
2775 0, /* tp_compare */
2766 0, /* tp_repr */
2776 0, /* tp_repr */
2767 0, /* tp_as_number */
2777 0, /* tp_as_number */
2768 &index_sequence_methods, /* tp_as_sequence */
2778 &index_sequence_methods, /* tp_as_sequence */
2769 &index_mapping_methods, /* tp_as_mapping */
2779 &index_mapping_methods, /* tp_as_mapping */
2770 0, /* tp_hash */
2780 0, /* tp_hash */
2771 0, /* tp_call */
2781 0, /* tp_call */
2772 0, /* tp_str */
2782 0, /* tp_str */
2773 0, /* tp_getattro */
2783 0, /* tp_getattro */
2774 0, /* tp_setattro */
2784 0, /* tp_setattro */
2775 0, /* tp_as_buffer */
2785 0, /* tp_as_buffer */
2776 Py_TPFLAGS_DEFAULT, /* tp_flags */
2786 Py_TPFLAGS_DEFAULT, /* tp_flags */
2777 "revlog index", /* tp_doc */
2787 "revlog index", /* tp_doc */
2778 0, /* tp_traverse */
2788 0, /* tp_traverse */
2779 0, /* tp_clear */
2789 0, /* tp_clear */
2780 0, /* tp_richcompare */
2790 0, /* tp_richcompare */
2781 0, /* tp_weaklistoffset */
2791 0, /* tp_weaklistoffset */
2782 0, /* tp_iter */
2792 0, /* tp_iter */
2783 0, /* tp_iternext */
2793 0, /* tp_iternext */
2784 index_methods, /* tp_methods */
2794 index_methods, /* tp_methods */
2785 0, /* tp_members */
2795 0, /* tp_members */
2786 index_getset, /* tp_getset */
2796 index_getset, /* tp_getset */
2787 0, /* tp_base */
2797 0, /* tp_base */
2788 0, /* tp_dict */
2798 0, /* tp_dict */
2789 0, /* tp_descr_get */
2799 0, /* tp_descr_get */
2790 0, /* tp_descr_set */
2800 0, /* tp_descr_set */
2791 0, /* tp_dictoffset */
2801 0, /* tp_dictoffset */
2792 (initproc)index_init, /* tp_init */
2802 (initproc)index_init, /* tp_init */
2793 0, /* tp_alloc */
2803 0, /* tp_alloc */
2794 };
2804 };
2795
2805
2796 /*
2806 /*
2797 * returns a tuple of the form (index, index, cache) with elements as
2807 * returns a tuple of the form (index, index, cache) with elements as
2798 * follows:
2808 * follows:
2799 *
2809 *
2800 * index: an index object that lazily parses RevlogNG records
2810 * index: an index object that lazily parses RevlogNG records
2801 * cache: if data is inlined, a tuple (0, index_file_content), else None
2811 * cache: if data is inlined, a tuple (0, index_file_content), else None
2802 * index_file_content could be a string, or a buffer
2812 * index_file_content could be a string, or a buffer
2803 *
2813 *
2804 * added complications are for backwards compatibility
2814 * added complications are for backwards compatibility
2805 */
2815 */
2806 PyObject *parse_index2(PyObject *self, PyObject *args)
2816 PyObject *parse_index2(PyObject *self, PyObject *args)
2807 {
2817 {
2808 PyObject *tuple = NULL, *cache = NULL;
2818 PyObject *tuple = NULL, *cache = NULL;
2809 indexObject *idx;
2819 indexObject *idx;
2810 int ret;
2820 int ret;
2811
2821
2812 idx = PyObject_New(indexObject, &HgRevlogIndex_Type);
2822 idx = PyObject_New(indexObject, &HgRevlogIndex_Type);
2813 if (idx == NULL)
2823 if (idx == NULL)
2814 goto bail;
2824 goto bail;
2815
2825
2816 ret = index_init(idx, args);
2826 ret = index_init(idx, args);
2817 if (ret == -1)
2827 if (ret == -1)
2818 goto bail;
2828 goto bail;
2819
2829
2820 if (idx->inlined) {
2830 if (idx->inlined) {
2821 cache = Py_BuildValue("iO", 0, idx->data);
2831 cache = Py_BuildValue("iO", 0, idx->data);
2822 if (cache == NULL)
2832 if (cache == NULL)
2823 goto bail;
2833 goto bail;
2824 } else {
2834 } else {
2825 cache = Py_None;
2835 cache = Py_None;
2826 Py_INCREF(cache);
2836 Py_INCREF(cache);
2827 }
2837 }
2828
2838
2829 tuple = Py_BuildValue("NN", idx, cache);
2839 tuple = Py_BuildValue("NN", idx, cache);
2830 if (!tuple)
2840 if (!tuple)
2831 goto bail;
2841 goto bail;
2832 return tuple;
2842 return tuple;
2833
2843
2834 bail:
2844 bail:
2835 Py_XDECREF(idx);
2845 Py_XDECREF(idx);
2836 Py_XDECREF(cache);
2846 Py_XDECREF(cache);
2837 Py_XDECREF(tuple);
2847 Py_XDECREF(tuple);
2838 return NULL;
2848 return NULL;
2839 }
2849 }
2840
2850
2841 #ifdef WITH_RUST
2851 #ifdef WITH_RUST
2842
2852
2843 /* rustlazyancestors: iteration over ancestors implemented in Rust
2853 /* rustlazyancestors: iteration over ancestors implemented in Rust
2844 *
2854 *
2845 * This class holds a reference to an index and to the Rust iterator.
2855 * This class holds a reference to an index and to the Rust iterator.
2846 */
2856 */
2847 typedef struct rustlazyancestorsObjectStruct rustlazyancestorsObject;
2857 typedef struct rustlazyancestorsObjectStruct rustlazyancestorsObject;
2848
2858
2849 struct rustlazyancestorsObjectStruct {
2859 struct rustlazyancestorsObjectStruct {
2850 PyObject_HEAD
2860 PyObject_HEAD
2851 /* Type-specific fields go here. */
2861 /* Type-specific fields go here. */
2852 indexObject *index; /* Ref kept to avoid GC'ing the index */
2862 indexObject *index; /* Ref kept to avoid GC'ing the index */
2853 void *iter; /* Rust iterator */
2863 void *iter; /* Rust iterator */
2854 };
2864 };
2855
2865
2856 /* FFI exposed from Rust code */
2866 /* FFI exposed from Rust code */
2857 rustlazyancestorsObject *rustlazyancestors_init(indexObject *index,
2867 rustlazyancestorsObject *rustlazyancestors_init(indexObject *index,
2858 /* intrevs vector */
2868 /* intrevs vector */
2859 Py_ssize_t initrevslen,
2869 Py_ssize_t initrevslen,
2860 long *initrevs, long stoprev,
2870 long *initrevs, long stoprev,
2861 int inclusive);
2871 int inclusive);
2862 void rustlazyancestors_drop(rustlazyancestorsObject *self);
2872 void rustlazyancestors_drop(rustlazyancestorsObject *self);
2863 int rustlazyancestors_next(rustlazyancestorsObject *self);
2873 int rustlazyancestors_next(rustlazyancestorsObject *self);
2864 int rustlazyancestors_contains(rustlazyancestorsObject *self, long rev);
2874 int rustlazyancestors_contains(rustlazyancestorsObject *self, long rev);
2865
2875
2866 /* CPython instance methods */
2876 /* CPython instance methods */
2867 static int rustla_init(rustlazyancestorsObject *self, PyObject *args)
2877 static int rustla_init(rustlazyancestorsObject *self, PyObject *args)
2868 {
2878 {
2869 PyObject *initrevsarg = NULL;
2879 PyObject *initrevsarg = NULL;
2870 PyObject *inclusivearg = NULL;
2880 PyObject *inclusivearg = NULL;
2871 long stoprev = 0;
2881 long stoprev = 0;
2872 long *initrevs = NULL;
2882 long *initrevs = NULL;
2873 int inclusive = 0;
2883 int inclusive = 0;
2874 Py_ssize_t i;
2884 Py_ssize_t i;
2875
2885
2876 indexObject *index;
2886 indexObject *index;
2877 if (!PyArg_ParseTuple(args, "O!O!lO!", &HgRevlogIndex_Type, &index,
2887 if (!PyArg_ParseTuple(args, "O!O!lO!", &HgRevlogIndex_Type, &index,
2878 &PyList_Type, &initrevsarg, &stoprev,
2888 &PyList_Type, &initrevsarg, &stoprev,
2879 &PyBool_Type, &inclusivearg))
2889 &PyBool_Type, &inclusivearg))
2880 return -1;
2890 return -1;
2881
2891
2882 Py_INCREF(index);
2892 Py_INCREF(index);
2883 self->index = index;
2893 self->index = index;
2884
2894
2885 if (inclusivearg == Py_True)
2895 if (inclusivearg == Py_True)
2886 inclusive = 1;
2896 inclusive = 1;
2887
2897
2888 Py_ssize_t linit = PyList_GET_SIZE(initrevsarg);
2898 Py_ssize_t linit = PyList_GET_SIZE(initrevsarg);
2889
2899
2890 initrevs = (long *)calloc(linit, sizeof(long));
2900 initrevs = (long *)calloc(linit, sizeof(long));
2891
2901
2892 if (initrevs == NULL) {
2902 if (initrevs == NULL) {
2893 PyErr_NoMemory();
2903 PyErr_NoMemory();
2894 goto bail;
2904 goto bail;
2895 }
2905 }
2896
2906
2897 for (i = 0; i < linit; i++) {
2907 for (i = 0; i < linit; i++) {
2898 initrevs[i] = PyInt_AsLong(PyList_GET_ITEM(initrevsarg, i));
2908 initrevs[i] = PyInt_AsLong(PyList_GET_ITEM(initrevsarg, i));
2899 }
2909 }
2900 if (PyErr_Occurred())
2910 if (PyErr_Occurred())
2901 goto bail;
2911 goto bail;
2902
2912
2903 self->iter =
2913 self->iter =
2904 rustlazyancestors_init(index, linit, initrevs, stoprev, inclusive);
2914 rustlazyancestors_init(index, linit, initrevs, stoprev, inclusive);
2905 if (self->iter == NULL) {
2915 if (self->iter == NULL) {
2906 /* if this is because of GraphError::ParentOutOfRange
2916 /* if this is because of GraphError::ParentOutOfRange
2907 * HgRevlogIndex_GetParents() has already set the proper
2917 * HgRevlogIndex_GetParents() has already set the proper
2908 * exception */
2918 * exception */
2909 goto bail;
2919 goto bail;
2910 }
2920 }
2911
2921
2912 free(initrevs);
2922 free(initrevs);
2913 return 0;
2923 return 0;
2914
2924
2915 bail:
2925 bail:
2916 free(initrevs);
2926 free(initrevs);
2917 return -1;
2927 return -1;
2918 };
2928 };
2919
2929
2920 static void rustla_dealloc(rustlazyancestorsObject *self)
2930 static void rustla_dealloc(rustlazyancestorsObject *self)
2921 {
2931 {
2922 Py_XDECREF(self->index);
2932 Py_XDECREF(self->index);
2923 if (self->iter != NULL) { /* can happen if rustla_init failed */
2933 if (self->iter != NULL) { /* can happen if rustla_init failed */
2924 rustlazyancestors_drop(self->iter);
2934 rustlazyancestors_drop(self->iter);
2925 }
2935 }
2926 PyObject_Del(self);
2936 PyObject_Del(self);
2927 }
2937 }
2928
2938
2929 static PyObject *rustla_next(rustlazyancestorsObject *self)
2939 static PyObject *rustla_next(rustlazyancestorsObject *self)
2930 {
2940 {
2931 int res = rustlazyancestors_next(self->iter);
2941 int res = rustlazyancestors_next(self->iter);
2932 if (res == -1) {
2942 if (res == -1) {
2933 /* Setting an explicit exception seems unnecessary
2943 /* Setting an explicit exception seems unnecessary
2934 * as examples from Python source code (Objects/rangeobjets.c
2944 * as examples from Python source code (Objects/rangeobjets.c
2935 * and Modules/_io/stringio.c) seem to demonstrate.
2945 * and Modules/_io/stringio.c) seem to demonstrate.
2936 */
2946 */
2937 return NULL;
2947 return NULL;
2938 }
2948 }
2939 return PyInt_FromLong(res);
2949 return PyInt_FromLong(res);
2940 }
2950 }
2941
2951
2942 static int rustla_contains(rustlazyancestorsObject *self, PyObject *rev)
2952 static int rustla_contains(rustlazyancestorsObject *self, PyObject *rev)
2943 {
2953 {
2944 long lrev;
2954 long lrev;
2945 if (!pylong_to_long(rev, &lrev)) {
2955 if (!pylong_to_long(rev, &lrev)) {
2946 PyErr_Clear();
2956 PyErr_Clear();
2947 return 0;
2957 return 0;
2948 }
2958 }
2949 return rustlazyancestors_contains(self->iter, lrev);
2959 return rustlazyancestors_contains(self->iter, lrev);
2950 }
2960 }
2951
2961
2952 static PySequenceMethods rustla_sequence_methods = {
2962 static PySequenceMethods rustla_sequence_methods = {
2953 0, /* sq_length */
2963 0, /* sq_length */
2954 0, /* sq_concat */
2964 0, /* sq_concat */
2955 0, /* sq_repeat */
2965 0, /* sq_repeat */
2956 0, /* sq_item */
2966 0, /* sq_item */
2957 0, /* sq_slice */
2967 0, /* sq_slice */
2958 0, /* sq_ass_item */
2968 0, /* sq_ass_item */
2959 0, /* sq_ass_slice */
2969 0, /* sq_ass_slice */
2960 (objobjproc)rustla_contains, /* sq_contains */
2970 (objobjproc)rustla_contains, /* sq_contains */
2961 };
2971 };
2962
2972
2963 static PyTypeObject rustlazyancestorsType = {
2973 static PyTypeObject rustlazyancestorsType = {
2964 PyVarObject_HEAD_INIT(NULL, 0) /* header */
2974 PyVarObject_HEAD_INIT(NULL, 0) /* header */
2965 "parsers.rustlazyancestors", /* tp_name */
2975 "parsers.rustlazyancestors", /* tp_name */
2966 sizeof(rustlazyancestorsObject), /* tp_basicsize */
2976 sizeof(rustlazyancestorsObject), /* tp_basicsize */
2967 0, /* tp_itemsize */
2977 0, /* tp_itemsize */
2968 (destructor)rustla_dealloc, /* tp_dealloc */
2978 (destructor)rustla_dealloc, /* tp_dealloc */
2969 0, /* tp_print */
2979 0, /* tp_print */
2970 0, /* tp_getattr */
2980 0, /* tp_getattr */
2971 0, /* tp_setattr */
2981 0, /* tp_setattr */
2972 0, /* tp_compare */
2982 0, /* tp_compare */
2973 0, /* tp_repr */
2983 0, /* tp_repr */
2974 0, /* tp_as_number */
2984 0, /* tp_as_number */
2975 &rustla_sequence_methods, /* tp_as_sequence */
2985 &rustla_sequence_methods, /* tp_as_sequence */
2976 0, /* tp_as_mapping */
2986 0, /* tp_as_mapping */
2977 0, /* tp_hash */
2987 0, /* tp_hash */
2978 0, /* tp_call */
2988 0, /* tp_call */
2979 0, /* tp_str */
2989 0, /* tp_str */
2980 0, /* tp_getattro */
2990 0, /* tp_getattro */
2981 0, /* tp_setattro */
2991 0, /* tp_setattro */
2982 0, /* tp_as_buffer */
2992 0, /* tp_as_buffer */
2983 Py_TPFLAGS_DEFAULT, /* tp_flags */
2993 Py_TPFLAGS_DEFAULT, /* tp_flags */
2984 "Iterator over ancestors, implemented in Rust", /* tp_doc */
2994 "Iterator over ancestors, implemented in Rust", /* tp_doc */
2985 0, /* tp_traverse */
2995 0, /* tp_traverse */
2986 0, /* tp_clear */
2996 0, /* tp_clear */
2987 0, /* tp_richcompare */
2997 0, /* tp_richcompare */
2988 0, /* tp_weaklistoffset */
2998 0, /* tp_weaklistoffset */
2989 0, /* tp_iter */
2999 0, /* tp_iter */
2990 (iternextfunc)rustla_next, /* tp_iternext */
3000 (iternextfunc)rustla_next, /* tp_iternext */
2991 0, /* tp_methods */
3001 0, /* tp_methods */
2992 0, /* tp_members */
3002 0, /* tp_members */
2993 0, /* tp_getset */
3003 0, /* tp_getset */
2994 0, /* tp_base */
3004 0, /* tp_base */
2995 0, /* tp_dict */
3005 0, /* tp_dict */
2996 0, /* tp_descr_get */
3006 0, /* tp_descr_get */
2997 0, /* tp_descr_set */
3007 0, /* tp_descr_set */
2998 0, /* tp_dictoffset */
3008 0, /* tp_dictoffset */
2999 (initproc)rustla_init, /* tp_init */
3009 (initproc)rustla_init, /* tp_init */
3000 0, /* tp_alloc */
3010 0, /* tp_alloc */
3001 };
3011 };
3002 #endif /* WITH_RUST */
3012 #endif /* WITH_RUST */
3003
3013
3004 void revlog_module_init(PyObject *mod)
3014 void revlog_module_init(PyObject *mod)
3005 {
3015 {
3006 PyObject *caps = NULL;
3016 PyObject *caps = NULL;
3007 HgRevlogIndex_Type.tp_new = PyType_GenericNew;
3017 HgRevlogIndex_Type.tp_new = PyType_GenericNew;
3008 if (PyType_Ready(&HgRevlogIndex_Type) < 0)
3018 if (PyType_Ready(&HgRevlogIndex_Type) < 0)
3009 return;
3019 return;
3010 Py_INCREF(&HgRevlogIndex_Type);
3020 Py_INCREF(&HgRevlogIndex_Type);
3011 PyModule_AddObject(mod, "index", (PyObject *)&HgRevlogIndex_Type);
3021 PyModule_AddObject(mod, "index", (PyObject *)&HgRevlogIndex_Type);
3012
3022
3013 nodetreeType.tp_new = PyType_GenericNew;
3023 nodetreeType.tp_new = PyType_GenericNew;
3014 if (PyType_Ready(&nodetreeType) < 0)
3024 if (PyType_Ready(&nodetreeType) < 0)
3015 return;
3025 return;
3016 Py_INCREF(&nodetreeType);
3026 Py_INCREF(&nodetreeType);
3017 PyModule_AddObject(mod, "nodetree", (PyObject *)&nodetreeType);
3027 PyModule_AddObject(mod, "nodetree", (PyObject *)&nodetreeType);
3018
3028
3019 if (!nullentry) {
3029 if (!nullentry) {
3020 nullentry =
3030 nullentry =
3021 Py_BuildValue(PY23("iiiiiiis#", "iiiiiiiy#"), 0, 0, 0, -1,
3031 Py_BuildValue(PY23("iiiiiiis#", "iiiiiiiy#"), 0, 0, 0, -1,
3022 -1, -1, -1, nullid, (Py_ssize_t)20);
3032 -1, -1, -1, nullid, (Py_ssize_t)20);
3023 }
3033 }
3024 if (nullentry)
3034 if (nullentry)
3025 PyObject_GC_UnTrack(nullentry);
3035 PyObject_GC_UnTrack(nullentry);
3026
3036
3027 caps = PyCapsule_New(HgRevlogIndex_GetParents,
3037 caps = PyCapsule_New(HgRevlogIndex_GetParents,
3028 "mercurial.cext.parsers.index_get_parents_CAPI",
3038 "mercurial.cext.parsers.index_get_parents_CAPI",
3029 NULL);
3039 NULL);
3030 if (caps != NULL)
3040 if (caps != NULL)
3031 PyModule_AddObject(mod, "index_get_parents_CAPI", caps);
3041 PyModule_AddObject(mod, "index_get_parents_CAPI", caps);
3032
3042
3033 #ifdef WITH_RUST
3043 #ifdef WITH_RUST
3034 rustlazyancestorsType.tp_new = PyType_GenericNew;
3044 rustlazyancestorsType.tp_new = PyType_GenericNew;
3035 if (PyType_Ready(&rustlazyancestorsType) < 0)
3045 if (PyType_Ready(&rustlazyancestorsType) < 0)
3036 return;
3046 return;
3037 Py_INCREF(&rustlazyancestorsType);
3047 Py_INCREF(&rustlazyancestorsType);
3038 PyModule_AddObject(mod, "rustlazyancestors",
3048 PyModule_AddObject(mod, "rustlazyancestors",
3039 (PyObject *)&rustlazyancestorsType);
3049 (PyObject *)&rustlazyancestorsType);
3040 #endif
3050 #endif
3041 }
3051 }
@@ -1,157 +1,157 b''
1 # policy.py - module policy logic for Mercurial.
1 # policy.py - module policy logic for Mercurial.
2 #
2 #
3 # Copyright 2015 Gregory Szorc <gregory.szorc@gmail.com>
3 # Copyright 2015 Gregory Szorc <gregory.szorc@gmail.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import os
10 import os
11 import sys
11 import sys
12
12
13 from .pycompat import getattr
13 from .pycompat import getattr
14
14
15 # Rules for how modules can be loaded. Values are:
15 # Rules for how modules can be loaded. Values are:
16 #
16 #
17 # c - require C extensions
17 # c - require C extensions
18 # rust+c - require Rust and C extensions
18 # rust+c - require Rust and C extensions
19 # rust+c-allow - allow Rust and C extensions with fallback to pure Python
19 # rust+c-allow - allow Rust and C extensions with fallback to pure Python
20 # for each
20 # for each
21 # allow - allow pure Python implementation when C loading fails
21 # allow - allow pure Python implementation when C loading fails
22 # cffi - required cffi versions (implemented within pure module)
22 # cffi - required cffi versions (implemented within pure module)
23 # cffi-allow - allow pure Python implementation if cffi version is missing
23 # cffi-allow - allow pure Python implementation if cffi version is missing
24 # py - only load pure Python modules
24 # py - only load pure Python modules
25 #
25 #
26 # By default, fall back to the pure modules so the in-place build can
26 # By default, fall back to the pure modules so the in-place build can
27 # run without recompiling the C extensions. This will be overridden by
27 # run without recompiling the C extensions. This will be overridden by
28 # __modulepolicy__ generated by setup.py.
28 # __modulepolicy__ generated by setup.py.
29 policy = b'allow'
29 policy = b'allow'
30 _packageprefs = {
30 _packageprefs = {
31 # policy: (versioned package, pure package)
31 # policy: (versioned package, pure package)
32 b'c': ('cext', None),
32 b'c': ('cext', None),
33 b'allow': ('cext', 'pure'),
33 b'allow': ('cext', 'pure'),
34 b'cffi': ('cffi', None),
34 b'cffi': ('cffi', None),
35 b'cffi-allow': ('cffi', 'pure'),
35 b'cffi-allow': ('cffi', 'pure'),
36 b'py': (None, 'pure'),
36 b'py': (None, 'pure'),
37 # For now, rust policies impact importrust only
37 # For now, rust policies impact importrust only
38 b'rust+c': ('cext', None),
38 b'rust+c': ('cext', None),
39 b'rust+c-allow': ('cext', 'pure'),
39 b'rust+c-allow': ('cext', 'pure'),
40 }
40 }
41
41
42 try:
42 try:
43 from . import __modulepolicy__
43 from . import __modulepolicy__
44
44
45 policy = __modulepolicy__.modulepolicy
45 policy = __modulepolicy__.modulepolicy
46 except ImportError:
46 except ImportError:
47 pass
47 pass
48
48
49 # PyPy doesn't load C extensions.
49 # PyPy doesn't load C extensions.
50 #
50 #
51 # The canonical way to do this is to test platform.python_implementation().
51 # The canonical way to do this is to test platform.python_implementation().
52 # But we don't import platform and don't bloat for it here.
52 # But we don't import platform and don't bloat for it here.
53 if '__pypy__' in sys.builtin_module_names:
53 if '__pypy__' in sys.builtin_module_names:
54 policy = b'cffi'
54 policy = b'cffi'
55
55
56 # Environment variable can always force settings.
56 # Environment variable can always force settings.
57 if sys.version_info[0] >= 3:
57 if sys.version_info[0] >= 3:
58 if 'HGMODULEPOLICY' in os.environ:
58 if 'HGMODULEPOLICY' in os.environ:
59 policy = os.environ['HGMODULEPOLICY'].encode('utf-8')
59 policy = os.environ['HGMODULEPOLICY'].encode('utf-8')
60 else:
60 else:
61 policy = os.environ.get('HGMODULEPOLICY', policy)
61 policy = os.environ.get('HGMODULEPOLICY', policy)
62
62
63
63
64 def _importfrom(pkgname, modname):
64 def _importfrom(pkgname, modname):
65 # from .<pkgname> import <modname> (where . is looked through this module)
65 # from .<pkgname> import <modname> (where . is looked through this module)
66 fakelocals = {}
66 fakelocals = {}
67 pkg = __import__(pkgname, globals(), fakelocals, [modname], level=1)
67 pkg = __import__(pkgname, globals(), fakelocals, [modname], level=1)
68 try:
68 try:
69 fakelocals[modname] = mod = getattr(pkg, modname)
69 fakelocals[modname] = mod = getattr(pkg, modname)
70 except AttributeError:
70 except AttributeError:
71 raise ImportError('cannot import name %s' % modname)
71 raise ImportError('cannot import name %s' % modname)
72 # force import; fakelocals[modname] may be replaced with the real module
72 # force import; fakelocals[modname] may be replaced with the real module
73 getattr(mod, '__doc__', None)
73 getattr(mod, '__doc__', None)
74 return fakelocals[modname]
74 return fakelocals[modname]
75
75
76
76
77 # keep in sync with "version" in C modules
77 # keep in sync with "version" in C modules
78 _cextversions = {
78 _cextversions = {
79 ('cext', 'base85'): 1,
79 ('cext', 'base85'): 1,
80 ('cext', 'bdiff'): 3,
80 ('cext', 'bdiff'): 3,
81 ('cext', 'mpatch'): 1,
81 ('cext', 'mpatch'): 1,
82 ('cext', 'osutil'): 4,
82 ('cext', 'osutil'): 4,
83 ('cext', 'parsers'): 13,
83 ('cext', 'parsers'): 14,
84 }
84 }
85
85
86 # map import request to other package or module
86 # map import request to other package or module
87 _modredirects = {
87 _modredirects = {
88 ('cext', 'charencode'): ('cext', 'parsers'),
88 ('cext', 'charencode'): ('cext', 'parsers'),
89 ('cffi', 'base85'): ('pure', 'base85'),
89 ('cffi', 'base85'): ('pure', 'base85'),
90 ('cffi', 'charencode'): ('pure', 'charencode'),
90 ('cffi', 'charencode'): ('pure', 'charencode'),
91 ('cffi', 'parsers'): ('pure', 'parsers'),
91 ('cffi', 'parsers'): ('pure', 'parsers'),
92 }
92 }
93
93
94
94
95 def _checkmod(pkgname, modname, mod):
95 def _checkmod(pkgname, modname, mod):
96 expected = _cextversions.get((pkgname, modname))
96 expected = _cextversions.get((pkgname, modname))
97 actual = getattr(mod, 'version', None)
97 actual = getattr(mod, 'version', None)
98 if actual != expected:
98 if actual != expected:
99 raise ImportError(
99 raise ImportError(
100 'cannot import module %s.%s '
100 'cannot import module %s.%s '
101 '(expected version: %d, actual: %r)'
101 '(expected version: %d, actual: %r)'
102 % (pkgname, modname, expected, actual)
102 % (pkgname, modname, expected, actual)
103 )
103 )
104
104
105
105
106 def importmod(modname):
106 def importmod(modname):
107 """Import module according to policy and check API version"""
107 """Import module according to policy and check API version"""
108 try:
108 try:
109 verpkg, purepkg = _packageprefs[policy]
109 verpkg, purepkg = _packageprefs[policy]
110 except KeyError:
110 except KeyError:
111 raise ImportError('invalid HGMODULEPOLICY %r' % policy)
111 raise ImportError('invalid HGMODULEPOLICY %r' % policy)
112 assert verpkg or purepkg
112 assert verpkg or purepkg
113 if verpkg:
113 if verpkg:
114 pn, mn = _modredirects.get((verpkg, modname), (verpkg, modname))
114 pn, mn = _modredirects.get((verpkg, modname), (verpkg, modname))
115 try:
115 try:
116 mod = _importfrom(pn, mn)
116 mod = _importfrom(pn, mn)
117 if pn == verpkg:
117 if pn == verpkg:
118 _checkmod(pn, mn, mod)
118 _checkmod(pn, mn, mod)
119 return mod
119 return mod
120 except ImportError:
120 except ImportError:
121 if not purepkg:
121 if not purepkg:
122 raise
122 raise
123 pn, mn = _modredirects.get((purepkg, modname), (purepkg, modname))
123 pn, mn = _modredirects.get((purepkg, modname), (purepkg, modname))
124 return _importfrom(pn, mn)
124 return _importfrom(pn, mn)
125
125
126
126
127 def _isrustpermissive():
127 def _isrustpermissive():
128 """Assuming the policy is a Rust one, tell if it's permissive."""
128 """Assuming the policy is a Rust one, tell if it's permissive."""
129 return policy.endswith(b'-allow')
129 return policy.endswith(b'-allow')
130
130
131
131
132 def importrust(modname, member=None, default=None):
132 def importrust(modname, member=None, default=None):
133 """Import Rust module according to policy and availability.
133 """Import Rust module according to policy and availability.
134
134
135 If policy isn't a Rust one, this returns `default`.
135 If policy isn't a Rust one, this returns `default`.
136
136
137 If either the module or its member is not available, this returns `default`
137 If either the module or its member is not available, this returns `default`
138 if policy is permissive and raises `ImportError` if not.
138 if policy is permissive and raises `ImportError` if not.
139 """
139 """
140 if not policy.startswith(b'rust'):
140 if not policy.startswith(b'rust'):
141 return default
141 return default
142
142
143 try:
143 try:
144 mod = _importfrom('rustext', modname)
144 mod = _importfrom('rustext', modname)
145 except ImportError:
145 except ImportError:
146 if _isrustpermissive():
146 if _isrustpermissive():
147 return default
147 return default
148 raise
148 raise
149 if member is None:
149 if member is None:
150 return mod
150 return mod
151
151
152 try:
152 try:
153 return getattr(mod, member)
153 return getattr(mod, member)
154 except AttributeError:
154 except AttributeError:
155 if _isrustpermissive():
155 if _isrustpermissive():
156 return default
156 return default
157 raise ImportError("Cannot import name %s" % member)
157 raise ImportError("Cannot import name %s" % member)
@@ -1,213 +1,217 b''
1 # parsers.py - Python implementation of parsers.c
1 # parsers.py - Python implementation of parsers.c
2 #
2 #
3 # Copyright 2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2009 Matt Mackall <mpm@selenic.com> and others
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import struct
10 import struct
11 import zlib
11 import zlib
12
12
13 from ..node import nullid, nullrev
13 from ..node import nullid, nullrev
14 from .. import (
14 from .. import (
15 pycompat,
15 pycompat,
16 revlogutils,
16 revlogutils,
17 util,
17 util,
18 )
18 )
19
19
20 stringio = pycompat.bytesio
20 stringio = pycompat.bytesio
21
21
22
22
23 _pack = struct.pack
23 _pack = struct.pack
24 _unpack = struct.unpack
24 _unpack = struct.unpack
25 _compress = zlib.compress
25 _compress = zlib.compress
26 _decompress = zlib.decompress
26 _decompress = zlib.decompress
27
27
28 # Some code below makes tuples directly because it's more convenient. However,
28 # Some code below makes tuples directly because it's more convenient. However,
29 # code outside this module should always use dirstatetuple.
29 # code outside this module should always use dirstatetuple.
30 def dirstatetuple(*x):
30 def dirstatetuple(*x):
31 # x is a tuple
31 # x is a tuple
32 return x
32 return x
33
33
34
34
35 indexformatng = b">Qiiiiii20s12x"
35 indexformatng = b">Qiiiiii20s12x"
36 indexfirst = struct.calcsize(b'Q')
36 indexfirst = struct.calcsize(b'Q')
37 sizeint = struct.calcsize(b'i')
37 sizeint = struct.calcsize(b'i')
38 indexsize = struct.calcsize(indexformatng)
38 indexsize = struct.calcsize(indexformatng)
39
39
40
40
41 def gettype(q):
41 def gettype(q):
42 return int(q & 0xFFFF)
42 return int(q & 0xFFFF)
43
43
44
44
45 def offset_type(offset, type):
45 def offset_type(offset, type):
46 return int(int(offset) << 16 | type)
46 return int(int(offset) << 16 | type)
47
47
48
48
49 class BaseIndexObject(object):
49 class BaseIndexObject(object):
50 @util.propertycache
50 @util.propertycache
51 def nodemap(self):
51 def nodemap(self):
52 nodemap = revlogutils.NodeMap({nullid: nullrev})
52 nodemap = revlogutils.NodeMap({nullid: nullrev})
53 for r in range(0, len(self)):
53 for r in range(0, len(self)):
54 n = self[r][7]
54 n = self[r][7]
55 nodemap[n] = r
55 nodemap[n] = r
56 return nodemap
56 return nodemap
57
57
58 def has_node(self, node):
59 """return True if the node exist in the index"""
60 return node in self.nodemap
61
58 def _stripnodes(self, start):
62 def _stripnodes(self, start):
59 if 'nodemap' in vars(self):
63 if 'nodemap' in vars(self):
60 for r in range(start, len(self)):
64 for r in range(start, len(self)):
61 n = self[r][7]
65 n = self[r][7]
62 del self.nodemap[n]
66 del self.nodemap[n]
63
67
64 def clearcaches(self):
68 def clearcaches(self):
65 self.__dict__.pop('nodemap', None)
69 self.__dict__.pop('nodemap', None)
66
70
67 def __len__(self):
71 def __len__(self):
68 return self._lgt + len(self._extra)
72 return self._lgt + len(self._extra)
69
73
70 def append(self, tup):
74 def append(self, tup):
71 if 'nodemap' in vars(self):
75 if 'nodemap' in vars(self):
72 self.nodemap[tup[7]] = len(self)
76 self.nodemap[tup[7]] = len(self)
73 self._extra.append(tup)
77 self._extra.append(tup)
74
78
75 def _check_index(self, i):
79 def _check_index(self, i):
76 if not isinstance(i, int):
80 if not isinstance(i, int):
77 raise TypeError(b"expecting int indexes")
81 raise TypeError(b"expecting int indexes")
78 if i < 0 or i >= len(self):
82 if i < 0 or i >= len(self):
79 raise IndexError
83 raise IndexError
80
84
81 def __getitem__(self, i):
85 def __getitem__(self, i):
82 if i == -1:
86 if i == -1:
83 return (0, 0, 0, -1, -1, -1, -1, nullid)
87 return (0, 0, 0, -1, -1, -1, -1, nullid)
84 self._check_index(i)
88 self._check_index(i)
85 if i >= self._lgt:
89 if i >= self._lgt:
86 return self._extra[i - self._lgt]
90 return self._extra[i - self._lgt]
87 index = self._calculate_index(i)
91 index = self._calculate_index(i)
88 r = struct.unpack(indexformatng, self._data[index : index + indexsize])
92 r = struct.unpack(indexformatng, self._data[index : index + indexsize])
89 if i == 0:
93 if i == 0:
90 e = list(r)
94 e = list(r)
91 type = gettype(e[0])
95 type = gettype(e[0])
92 e[0] = offset_type(0, type)
96 e[0] = offset_type(0, type)
93 return tuple(e)
97 return tuple(e)
94 return r
98 return r
95
99
96
100
97 class IndexObject(BaseIndexObject):
101 class IndexObject(BaseIndexObject):
98 def __init__(self, data):
102 def __init__(self, data):
99 assert len(data) % indexsize == 0
103 assert len(data) % indexsize == 0
100 self._data = data
104 self._data = data
101 self._lgt = len(data) // indexsize
105 self._lgt = len(data) // indexsize
102 self._extra = []
106 self._extra = []
103
107
104 def _calculate_index(self, i):
108 def _calculate_index(self, i):
105 return i * indexsize
109 return i * indexsize
106
110
107 def __delitem__(self, i):
111 def __delitem__(self, i):
108 if not isinstance(i, slice) or not i.stop == -1 or i.step is not None:
112 if not isinstance(i, slice) or not i.stop == -1 or i.step is not None:
109 raise ValueError(b"deleting slices only supports a:-1 with step 1")
113 raise ValueError(b"deleting slices only supports a:-1 with step 1")
110 i = i.start
114 i = i.start
111 self._check_index(i)
115 self._check_index(i)
112 self._stripnodes(i)
116 self._stripnodes(i)
113 if i < self._lgt:
117 if i < self._lgt:
114 self._data = self._data[: i * indexsize]
118 self._data = self._data[: i * indexsize]
115 self._lgt = i
119 self._lgt = i
116 self._extra = []
120 self._extra = []
117 else:
121 else:
118 self._extra = self._extra[: i - self._lgt]
122 self._extra = self._extra[: i - self._lgt]
119
123
120
124
121 class InlinedIndexObject(BaseIndexObject):
125 class InlinedIndexObject(BaseIndexObject):
122 def __init__(self, data, inline=0):
126 def __init__(self, data, inline=0):
123 self._data = data
127 self._data = data
124 self._lgt = self._inline_scan(None)
128 self._lgt = self._inline_scan(None)
125 self._inline_scan(self._lgt)
129 self._inline_scan(self._lgt)
126 self._extra = []
130 self._extra = []
127
131
128 def _inline_scan(self, lgt):
132 def _inline_scan(self, lgt):
129 off = 0
133 off = 0
130 if lgt is not None:
134 if lgt is not None:
131 self._offsets = [0] * lgt
135 self._offsets = [0] * lgt
132 count = 0
136 count = 0
133 while off <= len(self._data) - indexsize:
137 while off <= len(self._data) - indexsize:
134 (s,) = struct.unpack(
138 (s,) = struct.unpack(
135 b'>i', self._data[off + indexfirst : off + sizeint + indexfirst]
139 b'>i', self._data[off + indexfirst : off + sizeint + indexfirst]
136 )
140 )
137 if lgt is not None:
141 if lgt is not None:
138 self._offsets[count] = off
142 self._offsets[count] = off
139 count += 1
143 count += 1
140 off += indexsize + s
144 off += indexsize + s
141 if off != len(self._data):
145 if off != len(self._data):
142 raise ValueError(b"corrupted data")
146 raise ValueError(b"corrupted data")
143 return count
147 return count
144
148
145 def __delitem__(self, i):
149 def __delitem__(self, i):
146 if not isinstance(i, slice) or not i.stop == -1 or i.step is not None:
150 if not isinstance(i, slice) or not i.stop == -1 or i.step is not None:
147 raise ValueError(b"deleting slices only supports a:-1 with step 1")
151 raise ValueError(b"deleting slices only supports a:-1 with step 1")
148 i = i.start
152 i = i.start
149 self._check_index(i)
153 self._check_index(i)
150 self._stripnodes(i)
154 self._stripnodes(i)
151 if i < self._lgt:
155 if i < self._lgt:
152 self._offsets = self._offsets[:i]
156 self._offsets = self._offsets[:i]
153 self._lgt = i
157 self._lgt = i
154 self._extra = []
158 self._extra = []
155 else:
159 else:
156 self._extra = self._extra[: i - self._lgt]
160 self._extra = self._extra[: i - self._lgt]
157
161
158 def _calculate_index(self, i):
162 def _calculate_index(self, i):
159 return self._offsets[i]
163 return self._offsets[i]
160
164
161
165
162 def parse_index2(data, inline):
166 def parse_index2(data, inline):
163 if not inline:
167 if not inline:
164 return IndexObject(data), None
168 return IndexObject(data), None
165 return InlinedIndexObject(data, inline), (0, data)
169 return InlinedIndexObject(data, inline), (0, data)
166
170
167
171
168 def parse_dirstate(dmap, copymap, st):
172 def parse_dirstate(dmap, copymap, st):
169 parents = [st[:20], st[20:40]]
173 parents = [st[:20], st[20:40]]
170 # dereference fields so they will be local in loop
174 # dereference fields so they will be local in loop
171 format = b">cllll"
175 format = b">cllll"
172 e_size = struct.calcsize(format)
176 e_size = struct.calcsize(format)
173 pos1 = 40
177 pos1 = 40
174 l = len(st)
178 l = len(st)
175
179
176 # the inner loop
180 # the inner loop
177 while pos1 < l:
181 while pos1 < l:
178 pos2 = pos1 + e_size
182 pos2 = pos1 + e_size
179 e = _unpack(b">cllll", st[pos1:pos2]) # a literal here is faster
183 e = _unpack(b">cllll", st[pos1:pos2]) # a literal here is faster
180 pos1 = pos2 + e[4]
184 pos1 = pos2 + e[4]
181 f = st[pos2:pos1]
185 f = st[pos2:pos1]
182 if b'\0' in f:
186 if b'\0' in f:
183 f, c = f.split(b'\0')
187 f, c = f.split(b'\0')
184 copymap[f] = c
188 copymap[f] = c
185 dmap[f] = e[:4]
189 dmap[f] = e[:4]
186 return parents
190 return parents
187
191
188
192
189 def pack_dirstate(dmap, copymap, pl, now):
193 def pack_dirstate(dmap, copymap, pl, now):
190 now = int(now)
194 now = int(now)
191 cs = stringio()
195 cs = stringio()
192 write = cs.write
196 write = cs.write
193 write(b"".join(pl))
197 write(b"".join(pl))
194 for f, e in pycompat.iteritems(dmap):
198 for f, e in pycompat.iteritems(dmap):
195 if e[0] == b'n' and e[3] == now:
199 if e[0] == b'n' and e[3] == now:
196 # The file was last modified "simultaneously" with the current
200 # The file was last modified "simultaneously" with the current
197 # write to dirstate (i.e. within the same second for file-
201 # write to dirstate (i.e. within the same second for file-
198 # systems with a granularity of 1 sec). This commonly happens
202 # systems with a granularity of 1 sec). This commonly happens
199 # for at least a couple of files on 'update'.
203 # for at least a couple of files on 'update'.
200 # The user could change the file without changing its size
204 # The user could change the file without changing its size
201 # within the same second. Invalidate the file's mtime in
205 # within the same second. Invalidate the file's mtime in
202 # dirstate, forcing future 'status' calls to compare the
206 # dirstate, forcing future 'status' calls to compare the
203 # contents of the file if the size is the same. This prevents
207 # contents of the file if the size is the same. This prevents
204 # mistakenly treating such files as clean.
208 # mistakenly treating such files as clean.
205 e = dirstatetuple(e[0], e[1], e[2], -1)
209 e = dirstatetuple(e[0], e[1], e[2], -1)
206 dmap[f] = e
210 dmap[f] = e
207
211
208 if f in copymap:
212 if f in copymap:
209 f = b"%s\0%s" % (f, copymap[f])
213 f = b"%s\0%s" % (f, copymap[f])
210 e = _pack(b">cllll", e[0], e[1], e[2], e[3], len(f))
214 e = _pack(b">cllll", e[0], e[1], e[2], e[3], len(f))
211 write(e)
215 write(e)
212 write(f)
216 write(f)
213 return cs.getvalue()
217 return cs.getvalue()
@@ -1,2960 +1,2964 b''
1 # revlog.py - storage back-end for mercurial
1 # revlog.py - storage back-end for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 """Storage back-end for Mercurial.
8 """Storage back-end for Mercurial.
9
9
10 This provides efficient delta storage with O(1) retrieve and append
10 This provides efficient delta storage with O(1) retrieve and append
11 and O(changes) merge between branches.
11 and O(changes) merge between branches.
12 """
12 """
13
13
14 from __future__ import absolute_import
14 from __future__ import absolute_import
15
15
16 import collections
16 import collections
17 import contextlib
17 import contextlib
18 import errno
18 import errno
19 import io
19 import io
20 import os
20 import os
21 import struct
21 import struct
22 import zlib
22 import zlib
23
23
24 # import stuff from node for others to import from revlog
24 # import stuff from node for others to import from revlog
25 from .node import (
25 from .node import (
26 bin,
26 bin,
27 hex,
27 hex,
28 nullhex,
28 nullhex,
29 nullid,
29 nullid,
30 nullrev,
30 nullrev,
31 short,
31 short,
32 wdirfilenodeids,
32 wdirfilenodeids,
33 wdirhex,
33 wdirhex,
34 wdirid,
34 wdirid,
35 wdirrev,
35 wdirrev,
36 )
36 )
37 from .i18n import _
37 from .i18n import _
38 from .pycompat import getattr
38 from .pycompat import getattr
39 from .revlogutils.constants import (
39 from .revlogutils.constants import (
40 FLAG_GENERALDELTA,
40 FLAG_GENERALDELTA,
41 FLAG_INLINE_DATA,
41 FLAG_INLINE_DATA,
42 REVLOGV0,
42 REVLOGV0,
43 REVLOGV1,
43 REVLOGV1,
44 REVLOGV1_FLAGS,
44 REVLOGV1_FLAGS,
45 REVLOGV2,
45 REVLOGV2,
46 REVLOGV2_FLAGS,
46 REVLOGV2_FLAGS,
47 REVLOG_DEFAULT_FLAGS,
47 REVLOG_DEFAULT_FLAGS,
48 REVLOG_DEFAULT_FORMAT,
48 REVLOG_DEFAULT_FORMAT,
49 REVLOG_DEFAULT_VERSION,
49 REVLOG_DEFAULT_VERSION,
50 )
50 )
51 from .revlogutils.flagutil import (
51 from .revlogutils.flagutil import (
52 REVIDX_DEFAULT_FLAGS,
52 REVIDX_DEFAULT_FLAGS,
53 REVIDX_ELLIPSIS,
53 REVIDX_ELLIPSIS,
54 REVIDX_EXTSTORED,
54 REVIDX_EXTSTORED,
55 REVIDX_FLAGS_ORDER,
55 REVIDX_FLAGS_ORDER,
56 REVIDX_ISCENSORED,
56 REVIDX_ISCENSORED,
57 REVIDX_RAWTEXT_CHANGING_FLAGS,
57 REVIDX_RAWTEXT_CHANGING_FLAGS,
58 REVIDX_SIDEDATA,
58 REVIDX_SIDEDATA,
59 )
59 )
60 from .thirdparty import attr
60 from .thirdparty import attr
61 from . import (
61 from . import (
62 ancestor,
62 ancestor,
63 dagop,
63 dagop,
64 error,
64 error,
65 mdiff,
65 mdiff,
66 policy,
66 policy,
67 pycompat,
67 pycompat,
68 revlogutils,
68 revlogutils,
69 templatefilters,
69 templatefilters,
70 util,
70 util,
71 )
71 )
72 from .interfaces import (
72 from .interfaces import (
73 repository,
73 repository,
74 util as interfaceutil,
74 util as interfaceutil,
75 )
75 )
76 from .revlogutils import (
76 from .revlogutils import (
77 deltas as deltautil,
77 deltas as deltautil,
78 flagutil,
78 flagutil,
79 sidedata as sidedatautil,
79 sidedata as sidedatautil,
80 )
80 )
81 from .utils import (
81 from .utils import (
82 storageutil,
82 storageutil,
83 stringutil,
83 stringutil,
84 )
84 )
85
85
86 # blanked usage of all the name to prevent pyflakes constraints
86 # blanked usage of all the name to prevent pyflakes constraints
87 # We need these name available in the module for extensions.
87 # We need these name available in the module for extensions.
88 REVLOGV0
88 REVLOGV0
89 REVLOGV1
89 REVLOGV1
90 REVLOGV2
90 REVLOGV2
91 FLAG_INLINE_DATA
91 FLAG_INLINE_DATA
92 FLAG_GENERALDELTA
92 FLAG_GENERALDELTA
93 REVLOG_DEFAULT_FLAGS
93 REVLOG_DEFAULT_FLAGS
94 REVLOG_DEFAULT_FORMAT
94 REVLOG_DEFAULT_FORMAT
95 REVLOG_DEFAULT_VERSION
95 REVLOG_DEFAULT_VERSION
96 REVLOGV1_FLAGS
96 REVLOGV1_FLAGS
97 REVLOGV2_FLAGS
97 REVLOGV2_FLAGS
98 REVIDX_ISCENSORED
98 REVIDX_ISCENSORED
99 REVIDX_ELLIPSIS
99 REVIDX_ELLIPSIS
100 REVIDX_SIDEDATA
100 REVIDX_SIDEDATA
101 REVIDX_EXTSTORED
101 REVIDX_EXTSTORED
102 REVIDX_DEFAULT_FLAGS
102 REVIDX_DEFAULT_FLAGS
103 REVIDX_FLAGS_ORDER
103 REVIDX_FLAGS_ORDER
104 REVIDX_RAWTEXT_CHANGING_FLAGS
104 REVIDX_RAWTEXT_CHANGING_FLAGS
105
105
106 parsers = policy.importmod('parsers')
106 parsers = policy.importmod('parsers')
107 rustancestor = policy.importrust('ancestor')
107 rustancestor = policy.importrust('ancestor')
108 rustdagop = policy.importrust('dagop')
108 rustdagop = policy.importrust('dagop')
109
109
110 # Aliased for performance.
110 # Aliased for performance.
111 _zlibdecompress = zlib.decompress
111 _zlibdecompress = zlib.decompress
112
112
113 # max size of revlog with inline data
113 # max size of revlog with inline data
114 _maxinline = 131072
114 _maxinline = 131072
115 _chunksize = 1048576
115 _chunksize = 1048576
116
116
117 # Flag processors for REVIDX_ELLIPSIS.
117 # Flag processors for REVIDX_ELLIPSIS.
118 def ellipsisreadprocessor(rl, text):
118 def ellipsisreadprocessor(rl, text):
119 return text, False, {}
119 return text, False, {}
120
120
121
121
122 def ellipsiswriteprocessor(rl, text, sidedata):
122 def ellipsiswriteprocessor(rl, text, sidedata):
123 return text, False
123 return text, False
124
124
125
125
126 def ellipsisrawprocessor(rl, text):
126 def ellipsisrawprocessor(rl, text):
127 return False
127 return False
128
128
129
129
130 ellipsisprocessor = (
130 ellipsisprocessor = (
131 ellipsisreadprocessor,
131 ellipsisreadprocessor,
132 ellipsiswriteprocessor,
132 ellipsiswriteprocessor,
133 ellipsisrawprocessor,
133 ellipsisrawprocessor,
134 )
134 )
135
135
136
136
137 def getoffset(q):
137 def getoffset(q):
138 return int(q >> 16)
138 return int(q >> 16)
139
139
140
140
141 def gettype(q):
141 def gettype(q):
142 return int(q & 0xFFFF)
142 return int(q & 0xFFFF)
143
143
144
144
145 def offset_type(offset, type):
145 def offset_type(offset, type):
146 if (type & ~flagutil.REVIDX_KNOWN_FLAGS) != 0:
146 if (type & ~flagutil.REVIDX_KNOWN_FLAGS) != 0:
147 raise ValueError(b'unknown revlog index flags')
147 raise ValueError(b'unknown revlog index flags')
148 return int(int(offset) << 16 | type)
148 return int(int(offset) << 16 | type)
149
149
150
150
151 @attr.s(slots=True, frozen=True)
151 @attr.s(slots=True, frozen=True)
152 class _revisioninfo(object):
152 class _revisioninfo(object):
153 """Information about a revision that allows building its fulltext
153 """Information about a revision that allows building its fulltext
154 node: expected hash of the revision
154 node: expected hash of the revision
155 p1, p2: parent revs of the revision
155 p1, p2: parent revs of the revision
156 btext: built text cache consisting of a one-element list
156 btext: built text cache consisting of a one-element list
157 cachedelta: (baserev, uncompressed_delta) or None
157 cachedelta: (baserev, uncompressed_delta) or None
158 flags: flags associated to the revision storage
158 flags: flags associated to the revision storage
159
159
160 One of btext[0] or cachedelta must be set.
160 One of btext[0] or cachedelta must be set.
161 """
161 """
162
162
163 node = attr.ib()
163 node = attr.ib()
164 p1 = attr.ib()
164 p1 = attr.ib()
165 p2 = attr.ib()
165 p2 = attr.ib()
166 btext = attr.ib()
166 btext = attr.ib()
167 textlen = attr.ib()
167 textlen = attr.ib()
168 cachedelta = attr.ib()
168 cachedelta = attr.ib()
169 flags = attr.ib()
169 flags = attr.ib()
170
170
171
171
172 @interfaceutil.implementer(repository.irevisiondelta)
172 @interfaceutil.implementer(repository.irevisiondelta)
173 @attr.s(slots=True)
173 @attr.s(slots=True)
174 class revlogrevisiondelta(object):
174 class revlogrevisiondelta(object):
175 node = attr.ib()
175 node = attr.ib()
176 p1node = attr.ib()
176 p1node = attr.ib()
177 p2node = attr.ib()
177 p2node = attr.ib()
178 basenode = attr.ib()
178 basenode = attr.ib()
179 flags = attr.ib()
179 flags = attr.ib()
180 baserevisionsize = attr.ib()
180 baserevisionsize = attr.ib()
181 revision = attr.ib()
181 revision = attr.ib()
182 delta = attr.ib()
182 delta = attr.ib()
183 linknode = attr.ib(default=None)
183 linknode = attr.ib(default=None)
184
184
185
185
186 @interfaceutil.implementer(repository.iverifyproblem)
186 @interfaceutil.implementer(repository.iverifyproblem)
187 @attr.s(frozen=True)
187 @attr.s(frozen=True)
188 class revlogproblem(object):
188 class revlogproblem(object):
189 warning = attr.ib(default=None)
189 warning = attr.ib(default=None)
190 error = attr.ib(default=None)
190 error = attr.ib(default=None)
191 node = attr.ib(default=None)
191 node = attr.ib(default=None)
192
192
193
193
194 # index v0:
194 # index v0:
195 # 4 bytes: offset
195 # 4 bytes: offset
196 # 4 bytes: compressed length
196 # 4 bytes: compressed length
197 # 4 bytes: base rev
197 # 4 bytes: base rev
198 # 4 bytes: link rev
198 # 4 bytes: link rev
199 # 20 bytes: parent 1 nodeid
199 # 20 bytes: parent 1 nodeid
200 # 20 bytes: parent 2 nodeid
200 # 20 bytes: parent 2 nodeid
201 # 20 bytes: nodeid
201 # 20 bytes: nodeid
202 indexformatv0 = struct.Struct(b">4l20s20s20s")
202 indexformatv0 = struct.Struct(b">4l20s20s20s")
203 indexformatv0_pack = indexformatv0.pack
203 indexformatv0_pack = indexformatv0.pack
204 indexformatv0_unpack = indexformatv0.unpack
204 indexformatv0_unpack = indexformatv0.unpack
205
205
206
206
207 class revlogoldindex(list):
207 class revlogoldindex(list):
208 @util.propertycache
208 @util.propertycache
209 def nodemap(self):
209 def nodemap(self):
210 nodemap = revlogutils.NodeMap({nullid: nullrev})
210 nodemap = revlogutils.NodeMap({nullid: nullrev})
211 for r in range(0, len(self)):
211 for r in range(0, len(self)):
212 n = self[r][7]
212 n = self[r][7]
213 nodemap[n] = r
213 nodemap[n] = r
214 return nodemap
214 return nodemap
215
215
216 def has_node(self, node):
217 """return True if the node exist in the index"""
218 return node in self.nodemap
219
216 def append(self, tup):
220 def append(self, tup):
217 self.nodemap[tup[7]] = len(self)
221 self.nodemap[tup[7]] = len(self)
218 super(revlogoldindex, self).append(tup)
222 super(revlogoldindex, self).append(tup)
219
223
220 def __delitem__(self, i):
224 def __delitem__(self, i):
221 if not isinstance(i, slice) or not i.stop == -1 or i.step is not None:
225 if not isinstance(i, slice) or not i.stop == -1 or i.step is not None:
222 raise ValueError(b"deleting slices only supports a:-1 with step 1")
226 raise ValueError(b"deleting slices only supports a:-1 with step 1")
223 for r in pycompat.xrange(i.start, len(self)):
227 for r in pycompat.xrange(i.start, len(self)):
224 del self.nodemap[self[r][7]]
228 del self.nodemap[self[r][7]]
225 super(revlogoldindex, self).__delitem__(i)
229 super(revlogoldindex, self).__delitem__(i)
226
230
227 def clearcaches(self):
231 def clearcaches(self):
228 self.__dict__.pop('nodemap', None)
232 self.__dict__.pop('nodemap', None)
229
233
230 def __getitem__(self, i):
234 def __getitem__(self, i):
231 if i == -1:
235 if i == -1:
232 return (0, 0, 0, -1, -1, -1, -1, nullid)
236 return (0, 0, 0, -1, -1, -1, -1, nullid)
233 return list.__getitem__(self, i)
237 return list.__getitem__(self, i)
234
238
235
239
236 class revlogoldio(object):
240 class revlogoldio(object):
237 def __init__(self):
241 def __init__(self):
238 self.size = indexformatv0.size
242 self.size = indexformatv0.size
239
243
240 def parseindex(self, data, inline):
244 def parseindex(self, data, inline):
241 s = self.size
245 s = self.size
242 index = []
246 index = []
243 nodemap = revlogutils.NodeMap({nullid: nullrev})
247 nodemap = revlogutils.NodeMap({nullid: nullrev})
244 n = off = 0
248 n = off = 0
245 l = len(data)
249 l = len(data)
246 while off + s <= l:
250 while off + s <= l:
247 cur = data[off : off + s]
251 cur = data[off : off + s]
248 off += s
252 off += s
249 e = indexformatv0_unpack(cur)
253 e = indexformatv0_unpack(cur)
250 # transform to revlogv1 format
254 # transform to revlogv1 format
251 e2 = (
255 e2 = (
252 offset_type(e[0], 0),
256 offset_type(e[0], 0),
253 e[1],
257 e[1],
254 -1,
258 -1,
255 e[2],
259 e[2],
256 e[3],
260 e[3],
257 nodemap.get(e[4], nullrev),
261 nodemap.get(e[4], nullrev),
258 nodemap.get(e[5], nullrev),
262 nodemap.get(e[5], nullrev),
259 e[6],
263 e[6],
260 )
264 )
261 index.append(e2)
265 index.append(e2)
262 nodemap[e[6]] = n
266 nodemap[e[6]] = n
263 n += 1
267 n += 1
264
268
265 index = revlogoldindex(index)
269 index = revlogoldindex(index)
266 return index, None
270 return index, None
267
271
268 def packentry(self, entry, node, version, rev):
272 def packentry(self, entry, node, version, rev):
269 if gettype(entry[0]):
273 if gettype(entry[0]):
270 raise error.RevlogError(
274 raise error.RevlogError(
271 _(b'index entry flags need revlog version 1')
275 _(b'index entry flags need revlog version 1')
272 )
276 )
273 e2 = (
277 e2 = (
274 getoffset(entry[0]),
278 getoffset(entry[0]),
275 entry[1],
279 entry[1],
276 entry[3],
280 entry[3],
277 entry[4],
281 entry[4],
278 node(entry[5]),
282 node(entry[5]),
279 node(entry[6]),
283 node(entry[6]),
280 entry[7],
284 entry[7],
281 )
285 )
282 return indexformatv0_pack(*e2)
286 return indexformatv0_pack(*e2)
283
287
284
288
285 # index ng:
289 # index ng:
286 # 6 bytes: offset
290 # 6 bytes: offset
287 # 2 bytes: flags
291 # 2 bytes: flags
288 # 4 bytes: compressed length
292 # 4 bytes: compressed length
289 # 4 bytes: uncompressed length
293 # 4 bytes: uncompressed length
290 # 4 bytes: base rev
294 # 4 bytes: base rev
291 # 4 bytes: link rev
295 # 4 bytes: link rev
292 # 4 bytes: parent 1 rev
296 # 4 bytes: parent 1 rev
293 # 4 bytes: parent 2 rev
297 # 4 bytes: parent 2 rev
294 # 32 bytes: nodeid
298 # 32 bytes: nodeid
295 indexformatng = struct.Struct(b">Qiiiiii20s12x")
299 indexformatng = struct.Struct(b">Qiiiiii20s12x")
296 indexformatng_pack = indexformatng.pack
300 indexformatng_pack = indexformatng.pack
297 versionformat = struct.Struct(b">I")
301 versionformat = struct.Struct(b">I")
298 versionformat_pack = versionformat.pack
302 versionformat_pack = versionformat.pack
299 versionformat_unpack = versionformat.unpack
303 versionformat_unpack = versionformat.unpack
300
304
301 # corresponds to uncompressed length of indexformatng (2 gigs, 4-byte
305 # corresponds to uncompressed length of indexformatng (2 gigs, 4-byte
302 # signed integer)
306 # signed integer)
303 _maxentrysize = 0x7FFFFFFF
307 _maxentrysize = 0x7FFFFFFF
304
308
305
309
306 class revlogio(object):
310 class revlogio(object):
307 def __init__(self):
311 def __init__(self):
308 self.size = indexformatng.size
312 self.size = indexformatng.size
309
313
310 def parseindex(self, data, inline):
314 def parseindex(self, data, inline):
311 # call the C implementation to parse the index data
315 # call the C implementation to parse the index data
312 index, cache = parsers.parse_index2(data, inline)
316 index, cache = parsers.parse_index2(data, inline)
313 return index, cache
317 return index, cache
314
318
315 def packentry(self, entry, node, version, rev):
319 def packentry(self, entry, node, version, rev):
316 p = indexformatng_pack(*entry)
320 p = indexformatng_pack(*entry)
317 if rev == 0:
321 if rev == 0:
318 p = versionformat_pack(version) + p[4:]
322 p = versionformat_pack(version) + p[4:]
319 return p
323 return p
320
324
321
325
322 class revlog(object):
326 class revlog(object):
323 """
327 """
324 the underlying revision storage object
328 the underlying revision storage object
325
329
326 A revlog consists of two parts, an index and the revision data.
330 A revlog consists of two parts, an index and the revision data.
327
331
328 The index is a file with a fixed record size containing
332 The index is a file with a fixed record size containing
329 information on each revision, including its nodeid (hash), the
333 information on each revision, including its nodeid (hash), the
330 nodeids of its parents, the position and offset of its data within
334 nodeids of its parents, the position and offset of its data within
331 the data file, and the revision it's based on. Finally, each entry
335 the data file, and the revision it's based on. Finally, each entry
332 contains a linkrev entry that can serve as a pointer to external
336 contains a linkrev entry that can serve as a pointer to external
333 data.
337 data.
334
338
335 The revision data itself is a linear collection of data chunks.
339 The revision data itself is a linear collection of data chunks.
336 Each chunk represents a revision and is usually represented as a
340 Each chunk represents a revision and is usually represented as a
337 delta against the previous chunk. To bound lookup time, runs of
341 delta against the previous chunk. To bound lookup time, runs of
338 deltas are limited to about 2 times the length of the original
342 deltas are limited to about 2 times the length of the original
339 version data. This makes retrieval of a version proportional to
343 version data. This makes retrieval of a version proportional to
340 its size, or O(1) relative to the number of revisions.
344 its size, or O(1) relative to the number of revisions.
341
345
342 Both pieces of the revlog are written to in an append-only
346 Both pieces of the revlog are written to in an append-only
343 fashion, which means we never need to rewrite a file to insert or
347 fashion, which means we never need to rewrite a file to insert or
344 remove data, and can use some simple techniques to avoid the need
348 remove data, and can use some simple techniques to avoid the need
345 for locking while reading.
349 for locking while reading.
346
350
347 If checkambig, indexfile is opened with checkambig=True at
351 If checkambig, indexfile is opened with checkambig=True at
348 writing, to avoid file stat ambiguity.
352 writing, to avoid file stat ambiguity.
349
353
350 If mmaplargeindex is True, and an mmapindexthreshold is set, the
354 If mmaplargeindex is True, and an mmapindexthreshold is set, the
351 index will be mmapped rather than read if it is larger than the
355 index will be mmapped rather than read if it is larger than the
352 configured threshold.
356 configured threshold.
353
357
354 If censorable is True, the revlog can have censored revisions.
358 If censorable is True, the revlog can have censored revisions.
355
359
356 If `upperboundcomp` is not None, this is the expected maximal gain from
360 If `upperboundcomp` is not None, this is the expected maximal gain from
357 compression for the data content.
361 compression for the data content.
358 """
362 """
359
363
360 _flagserrorclass = error.RevlogError
364 _flagserrorclass = error.RevlogError
361
365
362 def __init__(
366 def __init__(
363 self,
367 self,
364 opener,
368 opener,
365 indexfile,
369 indexfile,
366 datafile=None,
370 datafile=None,
367 checkambig=False,
371 checkambig=False,
368 mmaplargeindex=False,
372 mmaplargeindex=False,
369 censorable=False,
373 censorable=False,
370 upperboundcomp=None,
374 upperboundcomp=None,
371 ):
375 ):
372 """
376 """
373 create a revlog object
377 create a revlog object
374
378
375 opener is a function that abstracts the file opening operation
379 opener is a function that abstracts the file opening operation
376 and can be used to implement COW semantics or the like.
380 and can be used to implement COW semantics or the like.
377
381
378 """
382 """
379 self.upperboundcomp = upperboundcomp
383 self.upperboundcomp = upperboundcomp
380 self.indexfile = indexfile
384 self.indexfile = indexfile
381 self.datafile = datafile or (indexfile[:-2] + b".d")
385 self.datafile = datafile or (indexfile[:-2] + b".d")
382 self.opener = opener
386 self.opener = opener
383 # When True, indexfile is opened with checkambig=True at writing, to
387 # When True, indexfile is opened with checkambig=True at writing, to
384 # avoid file stat ambiguity.
388 # avoid file stat ambiguity.
385 self._checkambig = checkambig
389 self._checkambig = checkambig
386 self._mmaplargeindex = mmaplargeindex
390 self._mmaplargeindex = mmaplargeindex
387 self._censorable = censorable
391 self._censorable = censorable
388 # 3-tuple of (node, rev, text) for a raw revision.
392 # 3-tuple of (node, rev, text) for a raw revision.
389 self._revisioncache = None
393 self._revisioncache = None
390 # Maps rev to chain base rev.
394 # Maps rev to chain base rev.
391 self._chainbasecache = util.lrucachedict(100)
395 self._chainbasecache = util.lrucachedict(100)
392 # 2-tuple of (offset, data) of raw data from the revlog at an offset.
396 # 2-tuple of (offset, data) of raw data from the revlog at an offset.
393 self._chunkcache = (0, b'')
397 self._chunkcache = (0, b'')
394 # How much data to read and cache into the raw revlog data cache.
398 # How much data to read and cache into the raw revlog data cache.
395 self._chunkcachesize = 65536
399 self._chunkcachesize = 65536
396 self._maxchainlen = None
400 self._maxchainlen = None
397 self._deltabothparents = True
401 self._deltabothparents = True
398 self.index = None
402 self.index = None
399 # Mapping of partial identifiers to full nodes.
403 # Mapping of partial identifiers to full nodes.
400 self._pcache = {}
404 self._pcache = {}
401 # Mapping of revision integer to full node.
405 # Mapping of revision integer to full node.
402 self._nodepos = None
406 self._nodepos = None
403 self._compengine = b'zlib'
407 self._compengine = b'zlib'
404 self._compengineopts = {}
408 self._compengineopts = {}
405 self._maxdeltachainspan = -1
409 self._maxdeltachainspan = -1
406 self._withsparseread = False
410 self._withsparseread = False
407 self._sparserevlog = False
411 self._sparserevlog = False
408 self._srdensitythreshold = 0.50
412 self._srdensitythreshold = 0.50
409 self._srmingapsize = 262144
413 self._srmingapsize = 262144
410
414
411 # Make copy of flag processors so each revlog instance can support
415 # Make copy of flag processors so each revlog instance can support
412 # custom flags.
416 # custom flags.
413 self._flagprocessors = dict(flagutil.flagprocessors)
417 self._flagprocessors = dict(flagutil.flagprocessors)
414
418
415 # 2-tuple of file handles being used for active writing.
419 # 2-tuple of file handles being used for active writing.
416 self._writinghandles = None
420 self._writinghandles = None
417
421
418 self._loadindex()
422 self._loadindex()
419
423
420 def _loadindex(self):
424 def _loadindex(self):
421 mmapindexthreshold = None
425 mmapindexthreshold = None
422 opts = self.opener.options
426 opts = self.opener.options
423
427
424 if b'revlogv2' in opts:
428 if b'revlogv2' in opts:
425 newversionflags = REVLOGV2 | FLAG_INLINE_DATA
429 newversionflags = REVLOGV2 | FLAG_INLINE_DATA
426 elif b'revlogv1' in opts:
430 elif b'revlogv1' in opts:
427 newversionflags = REVLOGV1 | FLAG_INLINE_DATA
431 newversionflags = REVLOGV1 | FLAG_INLINE_DATA
428 if b'generaldelta' in opts:
432 if b'generaldelta' in opts:
429 newversionflags |= FLAG_GENERALDELTA
433 newversionflags |= FLAG_GENERALDELTA
430 elif b'revlogv0' in self.opener.options:
434 elif b'revlogv0' in self.opener.options:
431 newversionflags = REVLOGV0
435 newversionflags = REVLOGV0
432 else:
436 else:
433 newversionflags = REVLOG_DEFAULT_VERSION
437 newversionflags = REVLOG_DEFAULT_VERSION
434
438
435 if b'chunkcachesize' in opts:
439 if b'chunkcachesize' in opts:
436 self._chunkcachesize = opts[b'chunkcachesize']
440 self._chunkcachesize = opts[b'chunkcachesize']
437 if b'maxchainlen' in opts:
441 if b'maxchainlen' in opts:
438 self._maxchainlen = opts[b'maxchainlen']
442 self._maxchainlen = opts[b'maxchainlen']
439 if b'deltabothparents' in opts:
443 if b'deltabothparents' in opts:
440 self._deltabothparents = opts[b'deltabothparents']
444 self._deltabothparents = opts[b'deltabothparents']
441 self._lazydelta = bool(opts.get(b'lazydelta', True))
445 self._lazydelta = bool(opts.get(b'lazydelta', True))
442 self._lazydeltabase = False
446 self._lazydeltabase = False
443 if self._lazydelta:
447 if self._lazydelta:
444 self._lazydeltabase = bool(opts.get(b'lazydeltabase', False))
448 self._lazydeltabase = bool(opts.get(b'lazydeltabase', False))
445 if b'compengine' in opts:
449 if b'compengine' in opts:
446 self._compengine = opts[b'compengine']
450 self._compengine = opts[b'compengine']
447 if b'zlib.level' in opts:
451 if b'zlib.level' in opts:
448 self._compengineopts[b'zlib.level'] = opts[b'zlib.level']
452 self._compengineopts[b'zlib.level'] = opts[b'zlib.level']
449 if b'zstd.level' in opts:
453 if b'zstd.level' in opts:
450 self._compengineopts[b'zstd.level'] = opts[b'zstd.level']
454 self._compengineopts[b'zstd.level'] = opts[b'zstd.level']
451 if b'maxdeltachainspan' in opts:
455 if b'maxdeltachainspan' in opts:
452 self._maxdeltachainspan = opts[b'maxdeltachainspan']
456 self._maxdeltachainspan = opts[b'maxdeltachainspan']
453 if self._mmaplargeindex and b'mmapindexthreshold' in opts:
457 if self._mmaplargeindex and b'mmapindexthreshold' in opts:
454 mmapindexthreshold = opts[b'mmapindexthreshold']
458 mmapindexthreshold = opts[b'mmapindexthreshold']
455 self.hassidedata = bool(opts.get(b'side-data', False))
459 self.hassidedata = bool(opts.get(b'side-data', False))
456 if self.hassidedata:
460 if self.hassidedata:
457 self._flagprocessors[REVIDX_SIDEDATA] = sidedatautil.processors
461 self._flagprocessors[REVIDX_SIDEDATA] = sidedatautil.processors
458 self._sparserevlog = bool(opts.get(b'sparse-revlog', False))
462 self._sparserevlog = bool(opts.get(b'sparse-revlog', False))
459 withsparseread = bool(opts.get(b'with-sparse-read', False))
463 withsparseread = bool(opts.get(b'with-sparse-read', False))
460 # sparse-revlog forces sparse-read
464 # sparse-revlog forces sparse-read
461 self._withsparseread = self._sparserevlog or withsparseread
465 self._withsparseread = self._sparserevlog or withsparseread
462 if b'sparse-read-density-threshold' in opts:
466 if b'sparse-read-density-threshold' in opts:
463 self._srdensitythreshold = opts[b'sparse-read-density-threshold']
467 self._srdensitythreshold = opts[b'sparse-read-density-threshold']
464 if b'sparse-read-min-gap-size' in opts:
468 if b'sparse-read-min-gap-size' in opts:
465 self._srmingapsize = opts[b'sparse-read-min-gap-size']
469 self._srmingapsize = opts[b'sparse-read-min-gap-size']
466 if opts.get(b'enableellipsis'):
470 if opts.get(b'enableellipsis'):
467 self._flagprocessors[REVIDX_ELLIPSIS] = ellipsisprocessor
471 self._flagprocessors[REVIDX_ELLIPSIS] = ellipsisprocessor
468
472
469 # revlog v0 doesn't have flag processors
473 # revlog v0 doesn't have flag processors
470 for flag, processor in pycompat.iteritems(
474 for flag, processor in pycompat.iteritems(
471 opts.get(b'flagprocessors', {})
475 opts.get(b'flagprocessors', {})
472 ):
476 ):
473 flagutil.insertflagprocessor(flag, processor, self._flagprocessors)
477 flagutil.insertflagprocessor(flag, processor, self._flagprocessors)
474
478
475 if self._chunkcachesize <= 0:
479 if self._chunkcachesize <= 0:
476 raise error.RevlogError(
480 raise error.RevlogError(
477 _(b'revlog chunk cache size %r is not greater than 0')
481 _(b'revlog chunk cache size %r is not greater than 0')
478 % self._chunkcachesize
482 % self._chunkcachesize
479 )
483 )
480 elif self._chunkcachesize & (self._chunkcachesize - 1):
484 elif self._chunkcachesize & (self._chunkcachesize - 1):
481 raise error.RevlogError(
485 raise error.RevlogError(
482 _(b'revlog chunk cache size %r is not a power of 2')
486 _(b'revlog chunk cache size %r is not a power of 2')
483 % self._chunkcachesize
487 % self._chunkcachesize
484 )
488 )
485
489
486 indexdata = b''
490 indexdata = b''
487 self._initempty = True
491 self._initempty = True
488 try:
492 try:
489 with self._indexfp() as f:
493 with self._indexfp() as f:
490 if (
494 if (
491 mmapindexthreshold is not None
495 mmapindexthreshold is not None
492 and self.opener.fstat(f).st_size >= mmapindexthreshold
496 and self.opener.fstat(f).st_size >= mmapindexthreshold
493 ):
497 ):
494 # TODO: should .close() to release resources without
498 # TODO: should .close() to release resources without
495 # relying on Python GC
499 # relying on Python GC
496 indexdata = util.buffer(util.mmapread(f))
500 indexdata = util.buffer(util.mmapread(f))
497 else:
501 else:
498 indexdata = f.read()
502 indexdata = f.read()
499 if len(indexdata) > 0:
503 if len(indexdata) > 0:
500 versionflags = versionformat_unpack(indexdata[:4])[0]
504 versionflags = versionformat_unpack(indexdata[:4])[0]
501 self._initempty = False
505 self._initempty = False
502 else:
506 else:
503 versionflags = newversionflags
507 versionflags = newversionflags
504 except IOError as inst:
508 except IOError as inst:
505 if inst.errno != errno.ENOENT:
509 if inst.errno != errno.ENOENT:
506 raise
510 raise
507
511
508 versionflags = newversionflags
512 versionflags = newversionflags
509
513
510 self.version = versionflags
514 self.version = versionflags
511
515
512 flags = versionflags & ~0xFFFF
516 flags = versionflags & ~0xFFFF
513 fmt = versionflags & 0xFFFF
517 fmt = versionflags & 0xFFFF
514
518
515 if fmt == REVLOGV0:
519 if fmt == REVLOGV0:
516 if flags:
520 if flags:
517 raise error.RevlogError(
521 raise error.RevlogError(
518 _(b'unknown flags (%#04x) in version %d revlog %s')
522 _(b'unknown flags (%#04x) in version %d revlog %s')
519 % (flags >> 16, fmt, self.indexfile)
523 % (flags >> 16, fmt, self.indexfile)
520 )
524 )
521
525
522 self._inline = False
526 self._inline = False
523 self._generaldelta = False
527 self._generaldelta = False
524
528
525 elif fmt == REVLOGV1:
529 elif fmt == REVLOGV1:
526 if flags & ~REVLOGV1_FLAGS:
530 if flags & ~REVLOGV1_FLAGS:
527 raise error.RevlogError(
531 raise error.RevlogError(
528 _(b'unknown flags (%#04x) in version %d revlog %s')
532 _(b'unknown flags (%#04x) in version %d revlog %s')
529 % (flags >> 16, fmt, self.indexfile)
533 % (flags >> 16, fmt, self.indexfile)
530 )
534 )
531
535
532 self._inline = versionflags & FLAG_INLINE_DATA
536 self._inline = versionflags & FLAG_INLINE_DATA
533 self._generaldelta = versionflags & FLAG_GENERALDELTA
537 self._generaldelta = versionflags & FLAG_GENERALDELTA
534
538
535 elif fmt == REVLOGV2:
539 elif fmt == REVLOGV2:
536 if flags & ~REVLOGV2_FLAGS:
540 if flags & ~REVLOGV2_FLAGS:
537 raise error.RevlogError(
541 raise error.RevlogError(
538 _(b'unknown flags (%#04x) in version %d revlog %s')
542 _(b'unknown flags (%#04x) in version %d revlog %s')
539 % (flags >> 16, fmt, self.indexfile)
543 % (flags >> 16, fmt, self.indexfile)
540 )
544 )
541
545
542 self._inline = versionflags & FLAG_INLINE_DATA
546 self._inline = versionflags & FLAG_INLINE_DATA
543 # generaldelta implied by version 2 revlogs.
547 # generaldelta implied by version 2 revlogs.
544 self._generaldelta = True
548 self._generaldelta = True
545
549
546 else:
550 else:
547 raise error.RevlogError(
551 raise error.RevlogError(
548 _(b'unknown version (%d) in revlog %s') % (fmt, self.indexfile)
552 _(b'unknown version (%d) in revlog %s') % (fmt, self.indexfile)
549 )
553 )
550 # sparse-revlog can't be on without general-delta (issue6056)
554 # sparse-revlog can't be on without general-delta (issue6056)
551 if not self._generaldelta:
555 if not self._generaldelta:
552 self._sparserevlog = False
556 self._sparserevlog = False
553
557
554 self._storedeltachains = True
558 self._storedeltachains = True
555
559
556 self._io = revlogio()
560 self._io = revlogio()
557 if self.version == REVLOGV0:
561 if self.version == REVLOGV0:
558 self._io = revlogoldio()
562 self._io = revlogoldio()
559 try:
563 try:
560 d = self._io.parseindex(indexdata, self._inline)
564 d = self._io.parseindex(indexdata, self._inline)
561 except (ValueError, IndexError):
565 except (ValueError, IndexError):
562 raise error.RevlogError(
566 raise error.RevlogError(
563 _(b"index %s is corrupted") % self.indexfile
567 _(b"index %s is corrupted") % self.indexfile
564 )
568 )
565 self.index, self._chunkcache = d
569 self.index, self._chunkcache = d
566 self.nodemap = self.index.nodemap
570 self.nodemap = self.index.nodemap
567 if not self._chunkcache:
571 if not self._chunkcache:
568 self._chunkclear()
572 self._chunkclear()
569 # revnum -> (chain-length, sum-delta-length)
573 # revnum -> (chain-length, sum-delta-length)
570 self._chaininfocache = {}
574 self._chaininfocache = {}
571 # revlog header -> revlog compressor
575 # revlog header -> revlog compressor
572 self._decompressors = {}
576 self._decompressors = {}
573
577
574 @util.propertycache
578 @util.propertycache
575 def _compressor(self):
579 def _compressor(self):
576 engine = util.compengines[self._compengine]
580 engine = util.compengines[self._compengine]
577 return engine.revlogcompressor(self._compengineopts)
581 return engine.revlogcompressor(self._compengineopts)
578
582
579 def _indexfp(self, mode=b'r'):
583 def _indexfp(self, mode=b'r'):
580 """file object for the revlog's index file"""
584 """file object for the revlog's index file"""
581 args = {'mode': mode}
585 args = {'mode': mode}
582 if mode != b'r':
586 if mode != b'r':
583 args['checkambig'] = self._checkambig
587 args['checkambig'] = self._checkambig
584 if mode == b'w':
588 if mode == b'w':
585 args['atomictemp'] = True
589 args['atomictemp'] = True
586 return self.opener(self.indexfile, **args)
590 return self.opener(self.indexfile, **args)
587
591
588 def _datafp(self, mode=b'r'):
592 def _datafp(self, mode=b'r'):
589 """file object for the revlog's data file"""
593 """file object for the revlog's data file"""
590 return self.opener(self.datafile, mode=mode)
594 return self.opener(self.datafile, mode=mode)
591
595
592 @contextlib.contextmanager
596 @contextlib.contextmanager
593 def _datareadfp(self, existingfp=None):
597 def _datareadfp(self, existingfp=None):
594 """file object suitable to read data"""
598 """file object suitable to read data"""
595 # Use explicit file handle, if given.
599 # Use explicit file handle, if given.
596 if existingfp is not None:
600 if existingfp is not None:
597 yield existingfp
601 yield existingfp
598
602
599 # Use a file handle being actively used for writes, if available.
603 # Use a file handle being actively used for writes, if available.
600 # There is some danger to doing this because reads will seek the
604 # There is some danger to doing this because reads will seek the
601 # file. However, _writeentry() performs a SEEK_END before all writes,
605 # file. However, _writeentry() performs a SEEK_END before all writes,
602 # so we should be safe.
606 # so we should be safe.
603 elif self._writinghandles:
607 elif self._writinghandles:
604 if self._inline:
608 if self._inline:
605 yield self._writinghandles[0]
609 yield self._writinghandles[0]
606 else:
610 else:
607 yield self._writinghandles[1]
611 yield self._writinghandles[1]
608
612
609 # Otherwise open a new file handle.
613 # Otherwise open a new file handle.
610 else:
614 else:
611 if self._inline:
615 if self._inline:
612 func = self._indexfp
616 func = self._indexfp
613 else:
617 else:
614 func = self._datafp
618 func = self._datafp
615 with func() as fp:
619 with func() as fp:
616 yield fp
620 yield fp
617
621
618 def tiprev(self):
622 def tiprev(self):
619 return len(self.index) - 1
623 return len(self.index) - 1
620
624
621 def tip(self):
625 def tip(self):
622 return self.node(self.tiprev())
626 return self.node(self.tiprev())
623
627
624 def __contains__(self, rev):
628 def __contains__(self, rev):
625 return 0 <= rev < len(self)
629 return 0 <= rev < len(self)
626
630
627 def __len__(self):
631 def __len__(self):
628 return len(self.index)
632 return len(self.index)
629
633
630 def __iter__(self):
634 def __iter__(self):
631 return iter(pycompat.xrange(len(self)))
635 return iter(pycompat.xrange(len(self)))
632
636
633 def revs(self, start=0, stop=None):
637 def revs(self, start=0, stop=None):
634 """iterate over all rev in this revlog (from start to stop)"""
638 """iterate over all rev in this revlog (from start to stop)"""
635 return storageutil.iterrevs(len(self), start=start, stop=stop)
639 return storageutil.iterrevs(len(self), start=start, stop=stop)
636
640
637 @util.propertycache
641 @util.propertycache
638 def nodemap(self):
642 def nodemap(self):
639 if self.index:
643 if self.index:
640 # populate mapping down to the initial node
644 # populate mapping down to the initial node
641 node0 = self.index[0][7] # get around changelog filtering
645 node0 = self.index[0][7] # get around changelog filtering
642 self.rev(node0)
646 self.rev(node0)
643 return self.index.nodemap
647 return self.index.nodemap
644
648
645 @property
649 @property
646 def _nodecache(self):
650 def _nodecache(self):
647 msg = "revlog._nodecache is deprecated, use revlog.index.nodemap"
651 msg = "revlog._nodecache is deprecated, use revlog.index.nodemap"
648 util.nouideprecwarn(msg, b'5.3', stacklevel=2)
652 util.nouideprecwarn(msg, b'5.3', stacklevel=2)
649 return self.index.nodemap
653 return self.index.nodemap
650
654
651 def hasnode(self, node):
655 def hasnode(self, node):
652 try:
656 try:
653 self.rev(node)
657 self.rev(node)
654 return True
658 return True
655 except KeyError:
659 except KeyError:
656 return False
660 return False
657
661
658 def candelta(self, baserev, rev):
662 def candelta(self, baserev, rev):
659 """whether two revisions (baserev, rev) can be delta-ed or not"""
663 """whether two revisions (baserev, rev) can be delta-ed or not"""
660 # Disable delta if either rev requires a content-changing flag
664 # Disable delta if either rev requires a content-changing flag
661 # processor (ex. LFS). This is because such flag processor can alter
665 # processor (ex. LFS). This is because such flag processor can alter
662 # the rawtext content that the delta will be based on, and two clients
666 # the rawtext content that the delta will be based on, and two clients
663 # could have a same revlog node with different flags (i.e. different
667 # could have a same revlog node with different flags (i.e. different
664 # rawtext contents) and the delta could be incompatible.
668 # rawtext contents) and the delta could be incompatible.
665 if (self.flags(baserev) & REVIDX_RAWTEXT_CHANGING_FLAGS) or (
669 if (self.flags(baserev) & REVIDX_RAWTEXT_CHANGING_FLAGS) or (
666 self.flags(rev) & REVIDX_RAWTEXT_CHANGING_FLAGS
670 self.flags(rev) & REVIDX_RAWTEXT_CHANGING_FLAGS
667 ):
671 ):
668 return False
672 return False
669 return True
673 return True
670
674
671 def clearcaches(self):
675 def clearcaches(self):
672 self._revisioncache = None
676 self._revisioncache = None
673 self._chainbasecache.clear()
677 self._chainbasecache.clear()
674 self._chunkcache = (0, b'')
678 self._chunkcache = (0, b'')
675 self._pcache = {}
679 self._pcache = {}
676 self.index.clearcaches()
680 self.index.clearcaches()
677
681
678 def rev(self, node):
682 def rev(self, node):
679 try:
683 try:
680 return self.index.nodemap[node]
684 return self.index.nodemap[node]
681 except TypeError:
685 except TypeError:
682 raise
686 raise
683 except error.RevlogError:
687 except error.RevlogError:
684 # parsers.c radix tree lookup failed
688 # parsers.c radix tree lookup failed
685 if node == wdirid or node in wdirfilenodeids:
689 if node == wdirid or node in wdirfilenodeids:
686 raise error.WdirUnsupported
690 raise error.WdirUnsupported
687 raise error.LookupError(node, self.indexfile, _(b'no node'))
691 raise error.LookupError(node, self.indexfile, _(b'no node'))
688
692
689 # Accessors for index entries.
693 # Accessors for index entries.
690
694
691 # First tuple entry is 8 bytes. First 6 bytes are offset. Last 2 bytes
695 # First tuple entry is 8 bytes. First 6 bytes are offset. Last 2 bytes
692 # are flags.
696 # are flags.
693 def start(self, rev):
697 def start(self, rev):
694 return int(self.index[rev][0] >> 16)
698 return int(self.index[rev][0] >> 16)
695
699
696 def flags(self, rev):
700 def flags(self, rev):
697 return self.index[rev][0] & 0xFFFF
701 return self.index[rev][0] & 0xFFFF
698
702
699 def length(self, rev):
703 def length(self, rev):
700 return self.index[rev][1]
704 return self.index[rev][1]
701
705
702 def rawsize(self, rev):
706 def rawsize(self, rev):
703 """return the length of the uncompressed text for a given revision"""
707 """return the length of the uncompressed text for a given revision"""
704 l = self.index[rev][2]
708 l = self.index[rev][2]
705 if l >= 0:
709 if l >= 0:
706 return l
710 return l
707
711
708 t = self.rawdata(rev)
712 t = self.rawdata(rev)
709 return len(t)
713 return len(t)
710
714
711 def size(self, rev):
715 def size(self, rev):
712 """length of non-raw text (processed by a "read" flag processor)"""
716 """length of non-raw text (processed by a "read" flag processor)"""
713 # fast path: if no "read" flag processor could change the content,
717 # fast path: if no "read" flag processor could change the content,
714 # size is rawsize. note: ELLIPSIS is known to not change the content.
718 # size is rawsize. note: ELLIPSIS is known to not change the content.
715 flags = self.flags(rev)
719 flags = self.flags(rev)
716 if flags & (flagutil.REVIDX_KNOWN_FLAGS ^ REVIDX_ELLIPSIS) == 0:
720 if flags & (flagutil.REVIDX_KNOWN_FLAGS ^ REVIDX_ELLIPSIS) == 0:
717 return self.rawsize(rev)
721 return self.rawsize(rev)
718
722
719 return len(self.revision(rev, raw=False))
723 return len(self.revision(rev, raw=False))
720
724
721 def chainbase(self, rev):
725 def chainbase(self, rev):
722 base = self._chainbasecache.get(rev)
726 base = self._chainbasecache.get(rev)
723 if base is not None:
727 if base is not None:
724 return base
728 return base
725
729
726 index = self.index
730 index = self.index
727 iterrev = rev
731 iterrev = rev
728 base = index[iterrev][3]
732 base = index[iterrev][3]
729 while base != iterrev:
733 while base != iterrev:
730 iterrev = base
734 iterrev = base
731 base = index[iterrev][3]
735 base = index[iterrev][3]
732
736
733 self._chainbasecache[rev] = base
737 self._chainbasecache[rev] = base
734 return base
738 return base
735
739
736 def linkrev(self, rev):
740 def linkrev(self, rev):
737 return self.index[rev][4]
741 return self.index[rev][4]
738
742
739 def parentrevs(self, rev):
743 def parentrevs(self, rev):
740 try:
744 try:
741 entry = self.index[rev]
745 entry = self.index[rev]
742 except IndexError:
746 except IndexError:
743 if rev == wdirrev:
747 if rev == wdirrev:
744 raise error.WdirUnsupported
748 raise error.WdirUnsupported
745 raise
749 raise
746
750
747 return entry[5], entry[6]
751 return entry[5], entry[6]
748
752
749 # fast parentrevs(rev) where rev isn't filtered
753 # fast parentrevs(rev) where rev isn't filtered
750 _uncheckedparentrevs = parentrevs
754 _uncheckedparentrevs = parentrevs
751
755
752 def node(self, rev):
756 def node(self, rev):
753 try:
757 try:
754 return self.index[rev][7]
758 return self.index[rev][7]
755 except IndexError:
759 except IndexError:
756 if rev == wdirrev:
760 if rev == wdirrev:
757 raise error.WdirUnsupported
761 raise error.WdirUnsupported
758 raise
762 raise
759
763
760 # Derived from index values.
764 # Derived from index values.
761
765
762 def end(self, rev):
766 def end(self, rev):
763 return self.start(rev) + self.length(rev)
767 return self.start(rev) + self.length(rev)
764
768
765 def parents(self, node):
769 def parents(self, node):
766 i = self.index
770 i = self.index
767 d = i[self.rev(node)]
771 d = i[self.rev(node)]
768 return i[d[5]][7], i[d[6]][7] # map revisions to nodes inline
772 return i[d[5]][7], i[d[6]][7] # map revisions to nodes inline
769
773
770 def chainlen(self, rev):
774 def chainlen(self, rev):
771 return self._chaininfo(rev)[0]
775 return self._chaininfo(rev)[0]
772
776
773 def _chaininfo(self, rev):
777 def _chaininfo(self, rev):
774 chaininfocache = self._chaininfocache
778 chaininfocache = self._chaininfocache
775 if rev in chaininfocache:
779 if rev in chaininfocache:
776 return chaininfocache[rev]
780 return chaininfocache[rev]
777 index = self.index
781 index = self.index
778 generaldelta = self._generaldelta
782 generaldelta = self._generaldelta
779 iterrev = rev
783 iterrev = rev
780 e = index[iterrev]
784 e = index[iterrev]
781 clen = 0
785 clen = 0
782 compresseddeltalen = 0
786 compresseddeltalen = 0
783 while iterrev != e[3]:
787 while iterrev != e[3]:
784 clen += 1
788 clen += 1
785 compresseddeltalen += e[1]
789 compresseddeltalen += e[1]
786 if generaldelta:
790 if generaldelta:
787 iterrev = e[3]
791 iterrev = e[3]
788 else:
792 else:
789 iterrev -= 1
793 iterrev -= 1
790 if iterrev in chaininfocache:
794 if iterrev in chaininfocache:
791 t = chaininfocache[iterrev]
795 t = chaininfocache[iterrev]
792 clen += t[0]
796 clen += t[0]
793 compresseddeltalen += t[1]
797 compresseddeltalen += t[1]
794 break
798 break
795 e = index[iterrev]
799 e = index[iterrev]
796 else:
800 else:
797 # Add text length of base since decompressing that also takes
801 # Add text length of base since decompressing that also takes
798 # work. For cache hits the length is already included.
802 # work. For cache hits the length is already included.
799 compresseddeltalen += e[1]
803 compresseddeltalen += e[1]
800 r = (clen, compresseddeltalen)
804 r = (clen, compresseddeltalen)
801 chaininfocache[rev] = r
805 chaininfocache[rev] = r
802 return r
806 return r
803
807
804 def _deltachain(self, rev, stoprev=None):
808 def _deltachain(self, rev, stoprev=None):
805 """Obtain the delta chain for a revision.
809 """Obtain the delta chain for a revision.
806
810
807 ``stoprev`` specifies a revision to stop at. If not specified, we
811 ``stoprev`` specifies a revision to stop at. If not specified, we
808 stop at the base of the chain.
812 stop at the base of the chain.
809
813
810 Returns a 2-tuple of (chain, stopped) where ``chain`` is a list of
814 Returns a 2-tuple of (chain, stopped) where ``chain`` is a list of
811 revs in ascending order and ``stopped`` is a bool indicating whether
815 revs in ascending order and ``stopped`` is a bool indicating whether
812 ``stoprev`` was hit.
816 ``stoprev`` was hit.
813 """
817 """
814 # Try C implementation.
818 # Try C implementation.
815 try:
819 try:
816 return self.index.deltachain(rev, stoprev, self._generaldelta)
820 return self.index.deltachain(rev, stoprev, self._generaldelta)
817 except AttributeError:
821 except AttributeError:
818 pass
822 pass
819
823
820 chain = []
824 chain = []
821
825
822 # Alias to prevent attribute lookup in tight loop.
826 # Alias to prevent attribute lookup in tight loop.
823 index = self.index
827 index = self.index
824 generaldelta = self._generaldelta
828 generaldelta = self._generaldelta
825
829
826 iterrev = rev
830 iterrev = rev
827 e = index[iterrev]
831 e = index[iterrev]
828 while iterrev != e[3] and iterrev != stoprev:
832 while iterrev != e[3] and iterrev != stoprev:
829 chain.append(iterrev)
833 chain.append(iterrev)
830 if generaldelta:
834 if generaldelta:
831 iterrev = e[3]
835 iterrev = e[3]
832 else:
836 else:
833 iterrev -= 1
837 iterrev -= 1
834 e = index[iterrev]
838 e = index[iterrev]
835
839
836 if iterrev == stoprev:
840 if iterrev == stoprev:
837 stopped = True
841 stopped = True
838 else:
842 else:
839 chain.append(iterrev)
843 chain.append(iterrev)
840 stopped = False
844 stopped = False
841
845
842 chain.reverse()
846 chain.reverse()
843 return chain, stopped
847 return chain, stopped
844
848
845 def ancestors(self, revs, stoprev=0, inclusive=False):
849 def ancestors(self, revs, stoprev=0, inclusive=False):
846 """Generate the ancestors of 'revs' in reverse revision order.
850 """Generate the ancestors of 'revs' in reverse revision order.
847 Does not generate revs lower than stoprev.
851 Does not generate revs lower than stoprev.
848
852
849 See the documentation for ancestor.lazyancestors for more details."""
853 See the documentation for ancestor.lazyancestors for more details."""
850
854
851 # first, make sure start revisions aren't filtered
855 # first, make sure start revisions aren't filtered
852 revs = list(revs)
856 revs = list(revs)
853 checkrev = self.node
857 checkrev = self.node
854 for r in revs:
858 for r in revs:
855 checkrev(r)
859 checkrev(r)
856 # and we're sure ancestors aren't filtered as well
860 # and we're sure ancestors aren't filtered as well
857
861
858 if rustancestor is not None:
862 if rustancestor is not None:
859 lazyancestors = rustancestor.LazyAncestors
863 lazyancestors = rustancestor.LazyAncestors
860 arg = self.index
864 arg = self.index
861 elif util.safehasattr(parsers, b'rustlazyancestors'):
865 elif util.safehasattr(parsers, b'rustlazyancestors'):
862 lazyancestors = ancestor.rustlazyancestors
866 lazyancestors = ancestor.rustlazyancestors
863 arg = self.index
867 arg = self.index
864 else:
868 else:
865 lazyancestors = ancestor.lazyancestors
869 lazyancestors = ancestor.lazyancestors
866 arg = self._uncheckedparentrevs
870 arg = self._uncheckedparentrevs
867 return lazyancestors(arg, revs, stoprev=stoprev, inclusive=inclusive)
871 return lazyancestors(arg, revs, stoprev=stoprev, inclusive=inclusive)
868
872
869 def descendants(self, revs):
873 def descendants(self, revs):
870 return dagop.descendantrevs(revs, self.revs, self.parentrevs)
874 return dagop.descendantrevs(revs, self.revs, self.parentrevs)
871
875
872 def findcommonmissing(self, common=None, heads=None):
876 def findcommonmissing(self, common=None, heads=None):
873 """Return a tuple of the ancestors of common and the ancestors of heads
877 """Return a tuple of the ancestors of common and the ancestors of heads
874 that are not ancestors of common. In revset terminology, we return the
878 that are not ancestors of common. In revset terminology, we return the
875 tuple:
879 tuple:
876
880
877 ::common, (::heads) - (::common)
881 ::common, (::heads) - (::common)
878
882
879 The list is sorted by revision number, meaning it is
883 The list is sorted by revision number, meaning it is
880 topologically sorted.
884 topologically sorted.
881
885
882 'heads' and 'common' are both lists of node IDs. If heads is
886 'heads' and 'common' are both lists of node IDs. If heads is
883 not supplied, uses all of the revlog's heads. If common is not
887 not supplied, uses all of the revlog's heads. If common is not
884 supplied, uses nullid."""
888 supplied, uses nullid."""
885 if common is None:
889 if common is None:
886 common = [nullid]
890 common = [nullid]
887 if heads is None:
891 if heads is None:
888 heads = self.heads()
892 heads = self.heads()
889
893
890 common = [self.rev(n) for n in common]
894 common = [self.rev(n) for n in common]
891 heads = [self.rev(n) for n in heads]
895 heads = [self.rev(n) for n in heads]
892
896
893 # we want the ancestors, but inclusive
897 # we want the ancestors, but inclusive
894 class lazyset(object):
898 class lazyset(object):
895 def __init__(self, lazyvalues):
899 def __init__(self, lazyvalues):
896 self.addedvalues = set()
900 self.addedvalues = set()
897 self.lazyvalues = lazyvalues
901 self.lazyvalues = lazyvalues
898
902
899 def __contains__(self, value):
903 def __contains__(self, value):
900 return value in self.addedvalues or value in self.lazyvalues
904 return value in self.addedvalues or value in self.lazyvalues
901
905
902 def __iter__(self):
906 def __iter__(self):
903 added = self.addedvalues
907 added = self.addedvalues
904 for r in added:
908 for r in added:
905 yield r
909 yield r
906 for r in self.lazyvalues:
910 for r in self.lazyvalues:
907 if not r in added:
911 if not r in added:
908 yield r
912 yield r
909
913
910 def add(self, value):
914 def add(self, value):
911 self.addedvalues.add(value)
915 self.addedvalues.add(value)
912
916
913 def update(self, values):
917 def update(self, values):
914 self.addedvalues.update(values)
918 self.addedvalues.update(values)
915
919
916 has = lazyset(self.ancestors(common))
920 has = lazyset(self.ancestors(common))
917 has.add(nullrev)
921 has.add(nullrev)
918 has.update(common)
922 has.update(common)
919
923
920 # take all ancestors from heads that aren't in has
924 # take all ancestors from heads that aren't in has
921 missing = set()
925 missing = set()
922 visit = collections.deque(r for r in heads if r not in has)
926 visit = collections.deque(r for r in heads if r not in has)
923 while visit:
927 while visit:
924 r = visit.popleft()
928 r = visit.popleft()
925 if r in missing:
929 if r in missing:
926 continue
930 continue
927 else:
931 else:
928 missing.add(r)
932 missing.add(r)
929 for p in self.parentrevs(r):
933 for p in self.parentrevs(r):
930 if p not in has:
934 if p not in has:
931 visit.append(p)
935 visit.append(p)
932 missing = list(missing)
936 missing = list(missing)
933 missing.sort()
937 missing.sort()
934 return has, [self.node(miss) for miss in missing]
938 return has, [self.node(miss) for miss in missing]
935
939
936 def incrementalmissingrevs(self, common=None):
940 def incrementalmissingrevs(self, common=None):
937 """Return an object that can be used to incrementally compute the
941 """Return an object that can be used to incrementally compute the
938 revision numbers of the ancestors of arbitrary sets that are not
942 revision numbers of the ancestors of arbitrary sets that are not
939 ancestors of common. This is an ancestor.incrementalmissingancestors
943 ancestors of common. This is an ancestor.incrementalmissingancestors
940 object.
944 object.
941
945
942 'common' is a list of revision numbers. If common is not supplied, uses
946 'common' is a list of revision numbers. If common is not supplied, uses
943 nullrev.
947 nullrev.
944 """
948 """
945 if common is None:
949 if common is None:
946 common = [nullrev]
950 common = [nullrev]
947
951
948 if rustancestor is not None:
952 if rustancestor is not None:
949 return rustancestor.MissingAncestors(self.index, common)
953 return rustancestor.MissingAncestors(self.index, common)
950 return ancestor.incrementalmissingancestors(self.parentrevs, common)
954 return ancestor.incrementalmissingancestors(self.parentrevs, common)
951
955
952 def findmissingrevs(self, common=None, heads=None):
956 def findmissingrevs(self, common=None, heads=None):
953 """Return the revision numbers of the ancestors of heads that
957 """Return the revision numbers of the ancestors of heads that
954 are not ancestors of common.
958 are not ancestors of common.
955
959
956 More specifically, return a list of revision numbers corresponding to
960 More specifically, return a list of revision numbers corresponding to
957 nodes N such that every N satisfies the following constraints:
961 nodes N such that every N satisfies the following constraints:
958
962
959 1. N is an ancestor of some node in 'heads'
963 1. N is an ancestor of some node in 'heads'
960 2. N is not an ancestor of any node in 'common'
964 2. N is not an ancestor of any node in 'common'
961
965
962 The list is sorted by revision number, meaning it is
966 The list is sorted by revision number, meaning it is
963 topologically sorted.
967 topologically sorted.
964
968
965 'heads' and 'common' are both lists of revision numbers. If heads is
969 'heads' and 'common' are both lists of revision numbers. If heads is
966 not supplied, uses all of the revlog's heads. If common is not
970 not supplied, uses all of the revlog's heads. If common is not
967 supplied, uses nullid."""
971 supplied, uses nullid."""
968 if common is None:
972 if common is None:
969 common = [nullrev]
973 common = [nullrev]
970 if heads is None:
974 if heads is None:
971 heads = self.headrevs()
975 heads = self.headrevs()
972
976
973 inc = self.incrementalmissingrevs(common=common)
977 inc = self.incrementalmissingrevs(common=common)
974 return inc.missingancestors(heads)
978 return inc.missingancestors(heads)
975
979
976 def findmissing(self, common=None, heads=None):
980 def findmissing(self, common=None, heads=None):
977 """Return the ancestors of heads that are not ancestors of common.
981 """Return the ancestors of heads that are not ancestors of common.
978
982
979 More specifically, return a list of nodes N such that every N
983 More specifically, return a list of nodes N such that every N
980 satisfies the following constraints:
984 satisfies the following constraints:
981
985
982 1. N is an ancestor of some node in 'heads'
986 1. N is an ancestor of some node in 'heads'
983 2. N is not an ancestor of any node in 'common'
987 2. N is not an ancestor of any node in 'common'
984
988
985 The list is sorted by revision number, meaning it is
989 The list is sorted by revision number, meaning it is
986 topologically sorted.
990 topologically sorted.
987
991
988 'heads' and 'common' are both lists of node IDs. If heads is
992 'heads' and 'common' are both lists of node IDs. If heads is
989 not supplied, uses all of the revlog's heads. If common is not
993 not supplied, uses all of the revlog's heads. If common is not
990 supplied, uses nullid."""
994 supplied, uses nullid."""
991 if common is None:
995 if common is None:
992 common = [nullid]
996 common = [nullid]
993 if heads is None:
997 if heads is None:
994 heads = self.heads()
998 heads = self.heads()
995
999
996 common = [self.rev(n) for n in common]
1000 common = [self.rev(n) for n in common]
997 heads = [self.rev(n) for n in heads]
1001 heads = [self.rev(n) for n in heads]
998
1002
999 inc = self.incrementalmissingrevs(common=common)
1003 inc = self.incrementalmissingrevs(common=common)
1000 return [self.node(r) for r in inc.missingancestors(heads)]
1004 return [self.node(r) for r in inc.missingancestors(heads)]
1001
1005
1002 def nodesbetween(self, roots=None, heads=None):
1006 def nodesbetween(self, roots=None, heads=None):
1003 """Return a topological path from 'roots' to 'heads'.
1007 """Return a topological path from 'roots' to 'heads'.
1004
1008
1005 Return a tuple (nodes, outroots, outheads) where 'nodes' is a
1009 Return a tuple (nodes, outroots, outheads) where 'nodes' is a
1006 topologically sorted list of all nodes N that satisfy both of
1010 topologically sorted list of all nodes N that satisfy both of
1007 these constraints:
1011 these constraints:
1008
1012
1009 1. N is a descendant of some node in 'roots'
1013 1. N is a descendant of some node in 'roots'
1010 2. N is an ancestor of some node in 'heads'
1014 2. N is an ancestor of some node in 'heads'
1011
1015
1012 Every node is considered to be both a descendant and an ancestor
1016 Every node is considered to be both a descendant and an ancestor
1013 of itself, so every reachable node in 'roots' and 'heads' will be
1017 of itself, so every reachable node in 'roots' and 'heads' will be
1014 included in 'nodes'.
1018 included in 'nodes'.
1015
1019
1016 'outroots' is the list of reachable nodes in 'roots', i.e., the
1020 'outroots' is the list of reachable nodes in 'roots', i.e., the
1017 subset of 'roots' that is returned in 'nodes'. Likewise,
1021 subset of 'roots' that is returned in 'nodes'. Likewise,
1018 'outheads' is the subset of 'heads' that is also in 'nodes'.
1022 'outheads' is the subset of 'heads' that is also in 'nodes'.
1019
1023
1020 'roots' and 'heads' are both lists of node IDs. If 'roots' is
1024 'roots' and 'heads' are both lists of node IDs. If 'roots' is
1021 unspecified, uses nullid as the only root. If 'heads' is
1025 unspecified, uses nullid as the only root. If 'heads' is
1022 unspecified, uses list of all of the revlog's heads."""
1026 unspecified, uses list of all of the revlog's heads."""
1023 nonodes = ([], [], [])
1027 nonodes = ([], [], [])
1024 if roots is not None:
1028 if roots is not None:
1025 roots = list(roots)
1029 roots = list(roots)
1026 if not roots:
1030 if not roots:
1027 return nonodes
1031 return nonodes
1028 lowestrev = min([self.rev(n) for n in roots])
1032 lowestrev = min([self.rev(n) for n in roots])
1029 else:
1033 else:
1030 roots = [nullid] # Everybody's a descendant of nullid
1034 roots = [nullid] # Everybody's a descendant of nullid
1031 lowestrev = nullrev
1035 lowestrev = nullrev
1032 if (lowestrev == nullrev) and (heads is None):
1036 if (lowestrev == nullrev) and (heads is None):
1033 # We want _all_ the nodes!
1037 # We want _all_ the nodes!
1034 return ([self.node(r) for r in self], [nullid], list(self.heads()))
1038 return ([self.node(r) for r in self], [nullid], list(self.heads()))
1035 if heads is None:
1039 if heads is None:
1036 # All nodes are ancestors, so the latest ancestor is the last
1040 # All nodes are ancestors, so the latest ancestor is the last
1037 # node.
1041 # node.
1038 highestrev = len(self) - 1
1042 highestrev = len(self) - 1
1039 # Set ancestors to None to signal that every node is an ancestor.
1043 # Set ancestors to None to signal that every node is an ancestor.
1040 ancestors = None
1044 ancestors = None
1041 # Set heads to an empty dictionary for later discovery of heads
1045 # Set heads to an empty dictionary for later discovery of heads
1042 heads = {}
1046 heads = {}
1043 else:
1047 else:
1044 heads = list(heads)
1048 heads = list(heads)
1045 if not heads:
1049 if not heads:
1046 return nonodes
1050 return nonodes
1047 ancestors = set()
1051 ancestors = set()
1048 # Turn heads into a dictionary so we can remove 'fake' heads.
1052 # Turn heads into a dictionary so we can remove 'fake' heads.
1049 # Also, later we will be using it to filter out the heads we can't
1053 # Also, later we will be using it to filter out the heads we can't
1050 # find from roots.
1054 # find from roots.
1051 heads = dict.fromkeys(heads, False)
1055 heads = dict.fromkeys(heads, False)
1052 # Start at the top and keep marking parents until we're done.
1056 # Start at the top and keep marking parents until we're done.
1053 nodestotag = set(heads)
1057 nodestotag = set(heads)
1054 # Remember where the top was so we can use it as a limit later.
1058 # Remember where the top was so we can use it as a limit later.
1055 highestrev = max([self.rev(n) for n in nodestotag])
1059 highestrev = max([self.rev(n) for n in nodestotag])
1056 while nodestotag:
1060 while nodestotag:
1057 # grab a node to tag
1061 # grab a node to tag
1058 n = nodestotag.pop()
1062 n = nodestotag.pop()
1059 # Never tag nullid
1063 # Never tag nullid
1060 if n == nullid:
1064 if n == nullid:
1061 continue
1065 continue
1062 # A node's revision number represents its place in a
1066 # A node's revision number represents its place in a
1063 # topologically sorted list of nodes.
1067 # topologically sorted list of nodes.
1064 r = self.rev(n)
1068 r = self.rev(n)
1065 if r >= lowestrev:
1069 if r >= lowestrev:
1066 if n not in ancestors:
1070 if n not in ancestors:
1067 # If we are possibly a descendant of one of the roots
1071 # If we are possibly a descendant of one of the roots
1068 # and we haven't already been marked as an ancestor
1072 # and we haven't already been marked as an ancestor
1069 ancestors.add(n) # Mark as ancestor
1073 ancestors.add(n) # Mark as ancestor
1070 # Add non-nullid parents to list of nodes to tag.
1074 # Add non-nullid parents to list of nodes to tag.
1071 nodestotag.update(
1075 nodestotag.update(
1072 [p for p in self.parents(n) if p != nullid]
1076 [p for p in self.parents(n) if p != nullid]
1073 )
1077 )
1074 elif n in heads: # We've seen it before, is it a fake head?
1078 elif n in heads: # We've seen it before, is it a fake head?
1075 # So it is, real heads should not be the ancestors of
1079 # So it is, real heads should not be the ancestors of
1076 # any other heads.
1080 # any other heads.
1077 heads.pop(n)
1081 heads.pop(n)
1078 if not ancestors:
1082 if not ancestors:
1079 return nonodes
1083 return nonodes
1080 # Now that we have our set of ancestors, we want to remove any
1084 # Now that we have our set of ancestors, we want to remove any
1081 # roots that are not ancestors.
1085 # roots that are not ancestors.
1082
1086
1083 # If one of the roots was nullid, everything is included anyway.
1087 # If one of the roots was nullid, everything is included anyway.
1084 if lowestrev > nullrev:
1088 if lowestrev > nullrev:
1085 # But, since we weren't, let's recompute the lowest rev to not
1089 # But, since we weren't, let's recompute the lowest rev to not
1086 # include roots that aren't ancestors.
1090 # include roots that aren't ancestors.
1087
1091
1088 # Filter out roots that aren't ancestors of heads
1092 # Filter out roots that aren't ancestors of heads
1089 roots = [root for root in roots if root in ancestors]
1093 roots = [root for root in roots if root in ancestors]
1090 # Recompute the lowest revision
1094 # Recompute the lowest revision
1091 if roots:
1095 if roots:
1092 lowestrev = min([self.rev(root) for root in roots])
1096 lowestrev = min([self.rev(root) for root in roots])
1093 else:
1097 else:
1094 # No more roots? Return empty list
1098 # No more roots? Return empty list
1095 return nonodes
1099 return nonodes
1096 else:
1100 else:
1097 # We are descending from nullid, and don't need to care about
1101 # We are descending from nullid, and don't need to care about
1098 # any other roots.
1102 # any other roots.
1099 lowestrev = nullrev
1103 lowestrev = nullrev
1100 roots = [nullid]
1104 roots = [nullid]
1101 # Transform our roots list into a set.
1105 # Transform our roots list into a set.
1102 descendants = set(roots)
1106 descendants = set(roots)
1103 # Also, keep the original roots so we can filter out roots that aren't
1107 # Also, keep the original roots so we can filter out roots that aren't
1104 # 'real' roots (i.e. are descended from other roots).
1108 # 'real' roots (i.e. are descended from other roots).
1105 roots = descendants.copy()
1109 roots = descendants.copy()
1106 # Our topologically sorted list of output nodes.
1110 # Our topologically sorted list of output nodes.
1107 orderedout = []
1111 orderedout = []
1108 # Don't start at nullid since we don't want nullid in our output list,
1112 # Don't start at nullid since we don't want nullid in our output list,
1109 # and if nullid shows up in descendants, empty parents will look like
1113 # and if nullid shows up in descendants, empty parents will look like
1110 # they're descendants.
1114 # they're descendants.
1111 for r in self.revs(start=max(lowestrev, 0), stop=highestrev + 1):
1115 for r in self.revs(start=max(lowestrev, 0), stop=highestrev + 1):
1112 n = self.node(r)
1116 n = self.node(r)
1113 isdescendant = False
1117 isdescendant = False
1114 if lowestrev == nullrev: # Everybody is a descendant of nullid
1118 if lowestrev == nullrev: # Everybody is a descendant of nullid
1115 isdescendant = True
1119 isdescendant = True
1116 elif n in descendants:
1120 elif n in descendants:
1117 # n is already a descendant
1121 # n is already a descendant
1118 isdescendant = True
1122 isdescendant = True
1119 # This check only needs to be done here because all the roots
1123 # This check only needs to be done here because all the roots
1120 # will start being marked is descendants before the loop.
1124 # will start being marked is descendants before the loop.
1121 if n in roots:
1125 if n in roots:
1122 # If n was a root, check if it's a 'real' root.
1126 # If n was a root, check if it's a 'real' root.
1123 p = tuple(self.parents(n))
1127 p = tuple(self.parents(n))
1124 # If any of its parents are descendants, it's not a root.
1128 # If any of its parents are descendants, it's not a root.
1125 if (p[0] in descendants) or (p[1] in descendants):
1129 if (p[0] in descendants) or (p[1] in descendants):
1126 roots.remove(n)
1130 roots.remove(n)
1127 else:
1131 else:
1128 p = tuple(self.parents(n))
1132 p = tuple(self.parents(n))
1129 # A node is a descendant if either of its parents are
1133 # A node is a descendant if either of its parents are
1130 # descendants. (We seeded the dependents list with the roots
1134 # descendants. (We seeded the dependents list with the roots
1131 # up there, remember?)
1135 # up there, remember?)
1132 if (p[0] in descendants) or (p[1] in descendants):
1136 if (p[0] in descendants) or (p[1] in descendants):
1133 descendants.add(n)
1137 descendants.add(n)
1134 isdescendant = True
1138 isdescendant = True
1135 if isdescendant and ((ancestors is None) or (n in ancestors)):
1139 if isdescendant and ((ancestors is None) or (n in ancestors)):
1136 # Only include nodes that are both descendants and ancestors.
1140 # Only include nodes that are both descendants and ancestors.
1137 orderedout.append(n)
1141 orderedout.append(n)
1138 if (ancestors is not None) and (n in heads):
1142 if (ancestors is not None) and (n in heads):
1139 # We're trying to figure out which heads are reachable
1143 # We're trying to figure out which heads are reachable
1140 # from roots.
1144 # from roots.
1141 # Mark this head as having been reached
1145 # Mark this head as having been reached
1142 heads[n] = True
1146 heads[n] = True
1143 elif ancestors is None:
1147 elif ancestors is None:
1144 # Otherwise, we're trying to discover the heads.
1148 # Otherwise, we're trying to discover the heads.
1145 # Assume this is a head because if it isn't, the next step
1149 # Assume this is a head because if it isn't, the next step
1146 # will eventually remove it.
1150 # will eventually remove it.
1147 heads[n] = True
1151 heads[n] = True
1148 # But, obviously its parents aren't.
1152 # But, obviously its parents aren't.
1149 for p in self.parents(n):
1153 for p in self.parents(n):
1150 heads.pop(p, None)
1154 heads.pop(p, None)
1151 heads = [head for head, flag in pycompat.iteritems(heads) if flag]
1155 heads = [head for head, flag in pycompat.iteritems(heads) if flag]
1152 roots = list(roots)
1156 roots = list(roots)
1153 assert orderedout
1157 assert orderedout
1154 assert roots
1158 assert roots
1155 assert heads
1159 assert heads
1156 return (orderedout, roots, heads)
1160 return (orderedout, roots, heads)
1157
1161
1158 def headrevs(self, revs=None):
1162 def headrevs(self, revs=None):
1159 if revs is None:
1163 if revs is None:
1160 try:
1164 try:
1161 return self.index.headrevs()
1165 return self.index.headrevs()
1162 except AttributeError:
1166 except AttributeError:
1163 return self._headrevs()
1167 return self._headrevs()
1164 if rustdagop is not None:
1168 if rustdagop is not None:
1165 return rustdagop.headrevs(self.index, revs)
1169 return rustdagop.headrevs(self.index, revs)
1166 return dagop.headrevs(revs, self._uncheckedparentrevs)
1170 return dagop.headrevs(revs, self._uncheckedparentrevs)
1167
1171
1168 def computephases(self, roots):
1172 def computephases(self, roots):
1169 return self.index.computephasesmapsets(roots)
1173 return self.index.computephasesmapsets(roots)
1170
1174
1171 def _headrevs(self):
1175 def _headrevs(self):
1172 count = len(self)
1176 count = len(self)
1173 if not count:
1177 if not count:
1174 return [nullrev]
1178 return [nullrev]
1175 # we won't iter over filtered rev so nobody is a head at start
1179 # we won't iter over filtered rev so nobody is a head at start
1176 ishead = [0] * (count + 1)
1180 ishead = [0] * (count + 1)
1177 index = self.index
1181 index = self.index
1178 for r in self:
1182 for r in self:
1179 ishead[r] = 1 # I may be an head
1183 ishead[r] = 1 # I may be an head
1180 e = index[r]
1184 e = index[r]
1181 ishead[e[5]] = ishead[e[6]] = 0 # my parent are not
1185 ishead[e[5]] = ishead[e[6]] = 0 # my parent are not
1182 return [r for r, val in enumerate(ishead) if val]
1186 return [r for r, val in enumerate(ishead) if val]
1183
1187
1184 def heads(self, start=None, stop=None):
1188 def heads(self, start=None, stop=None):
1185 """return the list of all nodes that have no children
1189 """return the list of all nodes that have no children
1186
1190
1187 if start is specified, only heads that are descendants of
1191 if start is specified, only heads that are descendants of
1188 start will be returned
1192 start will be returned
1189 if stop is specified, it will consider all the revs from stop
1193 if stop is specified, it will consider all the revs from stop
1190 as if they had no children
1194 as if they had no children
1191 """
1195 """
1192 if start is None and stop is None:
1196 if start is None and stop is None:
1193 if not len(self):
1197 if not len(self):
1194 return [nullid]
1198 return [nullid]
1195 return [self.node(r) for r in self.headrevs()]
1199 return [self.node(r) for r in self.headrevs()]
1196
1200
1197 if start is None:
1201 if start is None:
1198 start = nullrev
1202 start = nullrev
1199 else:
1203 else:
1200 start = self.rev(start)
1204 start = self.rev(start)
1201
1205
1202 stoprevs = set(self.rev(n) for n in stop or [])
1206 stoprevs = set(self.rev(n) for n in stop or [])
1203
1207
1204 revs = dagop.headrevssubset(
1208 revs = dagop.headrevssubset(
1205 self.revs, self.parentrevs, startrev=start, stoprevs=stoprevs
1209 self.revs, self.parentrevs, startrev=start, stoprevs=stoprevs
1206 )
1210 )
1207
1211
1208 return [self.node(rev) for rev in revs]
1212 return [self.node(rev) for rev in revs]
1209
1213
1210 def children(self, node):
1214 def children(self, node):
1211 """find the children of a given node"""
1215 """find the children of a given node"""
1212 c = []
1216 c = []
1213 p = self.rev(node)
1217 p = self.rev(node)
1214 for r in self.revs(start=p + 1):
1218 for r in self.revs(start=p + 1):
1215 prevs = [pr for pr in self.parentrevs(r) if pr != nullrev]
1219 prevs = [pr for pr in self.parentrevs(r) if pr != nullrev]
1216 if prevs:
1220 if prevs:
1217 for pr in prevs:
1221 for pr in prevs:
1218 if pr == p:
1222 if pr == p:
1219 c.append(self.node(r))
1223 c.append(self.node(r))
1220 elif p == nullrev:
1224 elif p == nullrev:
1221 c.append(self.node(r))
1225 c.append(self.node(r))
1222 return c
1226 return c
1223
1227
1224 def commonancestorsheads(self, a, b):
1228 def commonancestorsheads(self, a, b):
1225 """calculate all the heads of the common ancestors of nodes a and b"""
1229 """calculate all the heads of the common ancestors of nodes a and b"""
1226 a, b = self.rev(a), self.rev(b)
1230 a, b = self.rev(a), self.rev(b)
1227 ancs = self._commonancestorsheads(a, b)
1231 ancs = self._commonancestorsheads(a, b)
1228 return pycompat.maplist(self.node, ancs)
1232 return pycompat.maplist(self.node, ancs)
1229
1233
1230 def _commonancestorsheads(self, *revs):
1234 def _commonancestorsheads(self, *revs):
1231 """calculate all the heads of the common ancestors of revs"""
1235 """calculate all the heads of the common ancestors of revs"""
1232 try:
1236 try:
1233 ancs = self.index.commonancestorsheads(*revs)
1237 ancs = self.index.commonancestorsheads(*revs)
1234 except (AttributeError, OverflowError): # C implementation failed
1238 except (AttributeError, OverflowError): # C implementation failed
1235 ancs = ancestor.commonancestorsheads(self.parentrevs, *revs)
1239 ancs = ancestor.commonancestorsheads(self.parentrevs, *revs)
1236 return ancs
1240 return ancs
1237
1241
1238 def isancestor(self, a, b):
1242 def isancestor(self, a, b):
1239 """return True if node a is an ancestor of node b
1243 """return True if node a is an ancestor of node b
1240
1244
1241 A revision is considered an ancestor of itself."""
1245 A revision is considered an ancestor of itself."""
1242 a, b = self.rev(a), self.rev(b)
1246 a, b = self.rev(a), self.rev(b)
1243 return self.isancestorrev(a, b)
1247 return self.isancestorrev(a, b)
1244
1248
1245 def isancestorrev(self, a, b):
1249 def isancestorrev(self, a, b):
1246 """return True if revision a is an ancestor of revision b
1250 """return True if revision a is an ancestor of revision b
1247
1251
1248 A revision is considered an ancestor of itself.
1252 A revision is considered an ancestor of itself.
1249
1253
1250 The implementation of this is trivial but the use of
1254 The implementation of this is trivial but the use of
1251 reachableroots is not."""
1255 reachableroots is not."""
1252 if a == nullrev:
1256 if a == nullrev:
1253 return True
1257 return True
1254 elif a == b:
1258 elif a == b:
1255 return True
1259 return True
1256 elif a > b:
1260 elif a > b:
1257 return False
1261 return False
1258 return bool(self.reachableroots(a, [b], [a], includepath=False))
1262 return bool(self.reachableroots(a, [b], [a], includepath=False))
1259
1263
1260 def reachableroots(self, minroot, heads, roots, includepath=False):
1264 def reachableroots(self, minroot, heads, roots, includepath=False):
1261 """return (heads(::<roots> and <roots>::<heads>))
1265 """return (heads(::<roots> and <roots>::<heads>))
1262
1266
1263 If includepath is True, return (<roots>::<heads>)."""
1267 If includepath is True, return (<roots>::<heads>)."""
1264 try:
1268 try:
1265 return self.index.reachableroots2(
1269 return self.index.reachableroots2(
1266 minroot, heads, roots, includepath
1270 minroot, heads, roots, includepath
1267 )
1271 )
1268 except AttributeError:
1272 except AttributeError:
1269 return dagop._reachablerootspure(
1273 return dagop._reachablerootspure(
1270 self.parentrevs, minroot, roots, heads, includepath
1274 self.parentrevs, minroot, roots, heads, includepath
1271 )
1275 )
1272
1276
1273 def ancestor(self, a, b):
1277 def ancestor(self, a, b):
1274 """calculate the "best" common ancestor of nodes a and b"""
1278 """calculate the "best" common ancestor of nodes a and b"""
1275
1279
1276 a, b = self.rev(a), self.rev(b)
1280 a, b = self.rev(a), self.rev(b)
1277 try:
1281 try:
1278 ancs = self.index.ancestors(a, b)
1282 ancs = self.index.ancestors(a, b)
1279 except (AttributeError, OverflowError):
1283 except (AttributeError, OverflowError):
1280 ancs = ancestor.ancestors(self.parentrevs, a, b)
1284 ancs = ancestor.ancestors(self.parentrevs, a, b)
1281 if ancs:
1285 if ancs:
1282 # choose a consistent winner when there's a tie
1286 # choose a consistent winner when there's a tie
1283 return min(map(self.node, ancs))
1287 return min(map(self.node, ancs))
1284 return nullid
1288 return nullid
1285
1289
1286 def _match(self, id):
1290 def _match(self, id):
1287 if isinstance(id, int):
1291 if isinstance(id, int):
1288 # rev
1292 # rev
1289 return self.node(id)
1293 return self.node(id)
1290 if len(id) == 20:
1294 if len(id) == 20:
1291 # possibly a binary node
1295 # possibly a binary node
1292 # odds of a binary node being all hex in ASCII are 1 in 10**25
1296 # odds of a binary node being all hex in ASCII are 1 in 10**25
1293 try:
1297 try:
1294 node = id
1298 node = id
1295 self.rev(node) # quick search the index
1299 self.rev(node) # quick search the index
1296 return node
1300 return node
1297 except error.LookupError:
1301 except error.LookupError:
1298 pass # may be partial hex id
1302 pass # may be partial hex id
1299 try:
1303 try:
1300 # str(rev)
1304 # str(rev)
1301 rev = int(id)
1305 rev = int(id)
1302 if b"%d" % rev != id:
1306 if b"%d" % rev != id:
1303 raise ValueError
1307 raise ValueError
1304 if rev < 0:
1308 if rev < 0:
1305 rev = len(self) + rev
1309 rev = len(self) + rev
1306 if rev < 0 or rev >= len(self):
1310 if rev < 0 or rev >= len(self):
1307 raise ValueError
1311 raise ValueError
1308 return self.node(rev)
1312 return self.node(rev)
1309 except (ValueError, OverflowError):
1313 except (ValueError, OverflowError):
1310 pass
1314 pass
1311 if len(id) == 40:
1315 if len(id) == 40:
1312 try:
1316 try:
1313 # a full hex nodeid?
1317 # a full hex nodeid?
1314 node = bin(id)
1318 node = bin(id)
1315 self.rev(node)
1319 self.rev(node)
1316 return node
1320 return node
1317 except (TypeError, error.LookupError):
1321 except (TypeError, error.LookupError):
1318 pass
1322 pass
1319
1323
1320 def _partialmatch(self, id):
1324 def _partialmatch(self, id):
1321 # we don't care wdirfilenodeids as they should be always full hash
1325 # we don't care wdirfilenodeids as they should be always full hash
1322 maybewdir = wdirhex.startswith(id)
1326 maybewdir = wdirhex.startswith(id)
1323 try:
1327 try:
1324 partial = self.index.partialmatch(id)
1328 partial = self.index.partialmatch(id)
1325 if partial and self.hasnode(partial):
1329 if partial and self.hasnode(partial):
1326 if maybewdir:
1330 if maybewdir:
1327 # single 'ff...' match in radix tree, ambiguous with wdir
1331 # single 'ff...' match in radix tree, ambiguous with wdir
1328 raise error.RevlogError
1332 raise error.RevlogError
1329 return partial
1333 return partial
1330 if maybewdir:
1334 if maybewdir:
1331 # no 'ff...' match in radix tree, wdir identified
1335 # no 'ff...' match in radix tree, wdir identified
1332 raise error.WdirUnsupported
1336 raise error.WdirUnsupported
1333 return None
1337 return None
1334 except error.RevlogError:
1338 except error.RevlogError:
1335 # parsers.c radix tree lookup gave multiple matches
1339 # parsers.c radix tree lookup gave multiple matches
1336 # fast path: for unfiltered changelog, radix tree is accurate
1340 # fast path: for unfiltered changelog, radix tree is accurate
1337 if not getattr(self, 'filteredrevs', None):
1341 if not getattr(self, 'filteredrevs', None):
1338 raise error.AmbiguousPrefixLookupError(
1342 raise error.AmbiguousPrefixLookupError(
1339 id, self.indexfile, _(b'ambiguous identifier')
1343 id, self.indexfile, _(b'ambiguous identifier')
1340 )
1344 )
1341 # fall through to slow path that filters hidden revisions
1345 # fall through to slow path that filters hidden revisions
1342 except (AttributeError, ValueError):
1346 except (AttributeError, ValueError):
1343 # we are pure python, or key was too short to search radix tree
1347 # we are pure python, or key was too short to search radix tree
1344 pass
1348 pass
1345
1349
1346 if id in self._pcache:
1350 if id in self._pcache:
1347 return self._pcache[id]
1351 return self._pcache[id]
1348
1352
1349 if len(id) <= 40:
1353 if len(id) <= 40:
1350 try:
1354 try:
1351 # hex(node)[:...]
1355 # hex(node)[:...]
1352 l = len(id) // 2 # grab an even number of digits
1356 l = len(id) // 2 # grab an even number of digits
1353 prefix = bin(id[: l * 2])
1357 prefix = bin(id[: l * 2])
1354 nl = [e[7] for e in self.index if e[7].startswith(prefix)]
1358 nl = [e[7] for e in self.index if e[7].startswith(prefix)]
1355 nl = [
1359 nl = [
1356 n for n in nl if hex(n).startswith(id) and self.hasnode(n)
1360 n for n in nl if hex(n).startswith(id) and self.hasnode(n)
1357 ]
1361 ]
1358 if nullhex.startswith(id):
1362 if nullhex.startswith(id):
1359 nl.append(nullid)
1363 nl.append(nullid)
1360 if len(nl) > 0:
1364 if len(nl) > 0:
1361 if len(nl) == 1 and not maybewdir:
1365 if len(nl) == 1 and not maybewdir:
1362 self._pcache[id] = nl[0]
1366 self._pcache[id] = nl[0]
1363 return nl[0]
1367 return nl[0]
1364 raise error.AmbiguousPrefixLookupError(
1368 raise error.AmbiguousPrefixLookupError(
1365 id, self.indexfile, _(b'ambiguous identifier')
1369 id, self.indexfile, _(b'ambiguous identifier')
1366 )
1370 )
1367 if maybewdir:
1371 if maybewdir:
1368 raise error.WdirUnsupported
1372 raise error.WdirUnsupported
1369 return None
1373 return None
1370 except TypeError:
1374 except TypeError:
1371 pass
1375 pass
1372
1376
1373 def lookup(self, id):
1377 def lookup(self, id):
1374 """locate a node based on:
1378 """locate a node based on:
1375 - revision number or str(revision number)
1379 - revision number or str(revision number)
1376 - nodeid or subset of hex nodeid
1380 - nodeid or subset of hex nodeid
1377 """
1381 """
1378 n = self._match(id)
1382 n = self._match(id)
1379 if n is not None:
1383 if n is not None:
1380 return n
1384 return n
1381 n = self._partialmatch(id)
1385 n = self._partialmatch(id)
1382 if n:
1386 if n:
1383 return n
1387 return n
1384
1388
1385 raise error.LookupError(id, self.indexfile, _(b'no match found'))
1389 raise error.LookupError(id, self.indexfile, _(b'no match found'))
1386
1390
1387 def shortest(self, node, minlength=1):
1391 def shortest(self, node, minlength=1):
1388 """Find the shortest unambiguous prefix that matches node."""
1392 """Find the shortest unambiguous prefix that matches node."""
1389
1393
1390 def isvalid(prefix):
1394 def isvalid(prefix):
1391 try:
1395 try:
1392 matchednode = self._partialmatch(prefix)
1396 matchednode = self._partialmatch(prefix)
1393 except error.AmbiguousPrefixLookupError:
1397 except error.AmbiguousPrefixLookupError:
1394 return False
1398 return False
1395 except error.WdirUnsupported:
1399 except error.WdirUnsupported:
1396 # single 'ff...' match
1400 # single 'ff...' match
1397 return True
1401 return True
1398 if matchednode is None:
1402 if matchednode is None:
1399 raise error.LookupError(node, self.indexfile, _(b'no node'))
1403 raise error.LookupError(node, self.indexfile, _(b'no node'))
1400 return True
1404 return True
1401
1405
1402 def maybewdir(prefix):
1406 def maybewdir(prefix):
1403 return all(c == b'f' for c in pycompat.iterbytestr(prefix))
1407 return all(c == b'f' for c in pycompat.iterbytestr(prefix))
1404
1408
1405 hexnode = hex(node)
1409 hexnode = hex(node)
1406
1410
1407 def disambiguate(hexnode, minlength):
1411 def disambiguate(hexnode, minlength):
1408 """Disambiguate against wdirid."""
1412 """Disambiguate against wdirid."""
1409 for length in range(minlength, 41):
1413 for length in range(minlength, 41):
1410 prefix = hexnode[:length]
1414 prefix = hexnode[:length]
1411 if not maybewdir(prefix):
1415 if not maybewdir(prefix):
1412 return prefix
1416 return prefix
1413
1417
1414 if not getattr(self, 'filteredrevs', None):
1418 if not getattr(self, 'filteredrevs', None):
1415 try:
1419 try:
1416 length = max(self.index.shortest(node), minlength)
1420 length = max(self.index.shortest(node), minlength)
1417 return disambiguate(hexnode, length)
1421 return disambiguate(hexnode, length)
1418 except error.RevlogError:
1422 except error.RevlogError:
1419 if node != wdirid:
1423 if node != wdirid:
1420 raise error.LookupError(node, self.indexfile, _(b'no node'))
1424 raise error.LookupError(node, self.indexfile, _(b'no node'))
1421 except AttributeError:
1425 except AttributeError:
1422 # Fall through to pure code
1426 # Fall through to pure code
1423 pass
1427 pass
1424
1428
1425 if node == wdirid:
1429 if node == wdirid:
1426 for length in range(minlength, 41):
1430 for length in range(minlength, 41):
1427 prefix = hexnode[:length]
1431 prefix = hexnode[:length]
1428 if isvalid(prefix):
1432 if isvalid(prefix):
1429 return prefix
1433 return prefix
1430
1434
1431 for length in range(minlength, 41):
1435 for length in range(minlength, 41):
1432 prefix = hexnode[:length]
1436 prefix = hexnode[:length]
1433 if isvalid(prefix):
1437 if isvalid(prefix):
1434 return disambiguate(hexnode, length)
1438 return disambiguate(hexnode, length)
1435
1439
1436 def cmp(self, node, text):
1440 def cmp(self, node, text):
1437 """compare text with a given file revision
1441 """compare text with a given file revision
1438
1442
1439 returns True if text is different than what is stored.
1443 returns True if text is different than what is stored.
1440 """
1444 """
1441 p1, p2 = self.parents(node)
1445 p1, p2 = self.parents(node)
1442 return storageutil.hashrevisionsha1(text, p1, p2) != node
1446 return storageutil.hashrevisionsha1(text, p1, p2) != node
1443
1447
1444 def _cachesegment(self, offset, data):
1448 def _cachesegment(self, offset, data):
1445 """Add a segment to the revlog cache.
1449 """Add a segment to the revlog cache.
1446
1450
1447 Accepts an absolute offset and the data that is at that location.
1451 Accepts an absolute offset and the data that is at that location.
1448 """
1452 """
1449 o, d = self._chunkcache
1453 o, d = self._chunkcache
1450 # try to add to existing cache
1454 # try to add to existing cache
1451 if o + len(d) == offset and len(d) + len(data) < _chunksize:
1455 if o + len(d) == offset and len(d) + len(data) < _chunksize:
1452 self._chunkcache = o, d + data
1456 self._chunkcache = o, d + data
1453 else:
1457 else:
1454 self._chunkcache = offset, data
1458 self._chunkcache = offset, data
1455
1459
1456 def _readsegment(self, offset, length, df=None):
1460 def _readsegment(self, offset, length, df=None):
1457 """Load a segment of raw data from the revlog.
1461 """Load a segment of raw data from the revlog.
1458
1462
1459 Accepts an absolute offset, length to read, and an optional existing
1463 Accepts an absolute offset, length to read, and an optional existing
1460 file handle to read from.
1464 file handle to read from.
1461
1465
1462 If an existing file handle is passed, it will be seeked and the
1466 If an existing file handle is passed, it will be seeked and the
1463 original seek position will NOT be restored.
1467 original seek position will NOT be restored.
1464
1468
1465 Returns a str or buffer of raw byte data.
1469 Returns a str or buffer of raw byte data.
1466
1470
1467 Raises if the requested number of bytes could not be read.
1471 Raises if the requested number of bytes could not be read.
1468 """
1472 """
1469 # Cache data both forward and backward around the requested
1473 # Cache data both forward and backward around the requested
1470 # data, in a fixed size window. This helps speed up operations
1474 # data, in a fixed size window. This helps speed up operations
1471 # involving reading the revlog backwards.
1475 # involving reading the revlog backwards.
1472 cachesize = self._chunkcachesize
1476 cachesize = self._chunkcachesize
1473 realoffset = offset & ~(cachesize - 1)
1477 realoffset = offset & ~(cachesize - 1)
1474 reallength = (
1478 reallength = (
1475 (offset + length + cachesize) & ~(cachesize - 1)
1479 (offset + length + cachesize) & ~(cachesize - 1)
1476 ) - realoffset
1480 ) - realoffset
1477 with self._datareadfp(df) as df:
1481 with self._datareadfp(df) as df:
1478 df.seek(realoffset)
1482 df.seek(realoffset)
1479 d = df.read(reallength)
1483 d = df.read(reallength)
1480
1484
1481 self._cachesegment(realoffset, d)
1485 self._cachesegment(realoffset, d)
1482 if offset != realoffset or reallength != length:
1486 if offset != realoffset or reallength != length:
1483 startoffset = offset - realoffset
1487 startoffset = offset - realoffset
1484 if len(d) - startoffset < length:
1488 if len(d) - startoffset < length:
1485 raise error.RevlogError(
1489 raise error.RevlogError(
1486 _(
1490 _(
1487 b'partial read of revlog %s; expected %d bytes from '
1491 b'partial read of revlog %s; expected %d bytes from '
1488 b'offset %d, got %d'
1492 b'offset %d, got %d'
1489 )
1493 )
1490 % (
1494 % (
1491 self.indexfile if self._inline else self.datafile,
1495 self.indexfile if self._inline else self.datafile,
1492 length,
1496 length,
1493 realoffset,
1497 realoffset,
1494 len(d) - startoffset,
1498 len(d) - startoffset,
1495 )
1499 )
1496 )
1500 )
1497
1501
1498 return util.buffer(d, startoffset, length)
1502 return util.buffer(d, startoffset, length)
1499
1503
1500 if len(d) < length:
1504 if len(d) < length:
1501 raise error.RevlogError(
1505 raise error.RevlogError(
1502 _(
1506 _(
1503 b'partial read of revlog %s; expected %d bytes from offset '
1507 b'partial read of revlog %s; expected %d bytes from offset '
1504 b'%d, got %d'
1508 b'%d, got %d'
1505 )
1509 )
1506 % (
1510 % (
1507 self.indexfile if self._inline else self.datafile,
1511 self.indexfile if self._inline else self.datafile,
1508 length,
1512 length,
1509 offset,
1513 offset,
1510 len(d),
1514 len(d),
1511 )
1515 )
1512 )
1516 )
1513
1517
1514 return d
1518 return d
1515
1519
1516 def _getsegment(self, offset, length, df=None):
1520 def _getsegment(self, offset, length, df=None):
1517 """Obtain a segment of raw data from the revlog.
1521 """Obtain a segment of raw data from the revlog.
1518
1522
1519 Accepts an absolute offset, length of bytes to obtain, and an
1523 Accepts an absolute offset, length of bytes to obtain, and an
1520 optional file handle to the already-opened revlog. If the file
1524 optional file handle to the already-opened revlog. If the file
1521 handle is used, it's original seek position will not be preserved.
1525 handle is used, it's original seek position will not be preserved.
1522
1526
1523 Requests for data may be returned from a cache.
1527 Requests for data may be returned from a cache.
1524
1528
1525 Returns a str or a buffer instance of raw byte data.
1529 Returns a str or a buffer instance of raw byte data.
1526 """
1530 """
1527 o, d = self._chunkcache
1531 o, d = self._chunkcache
1528 l = len(d)
1532 l = len(d)
1529
1533
1530 # is it in the cache?
1534 # is it in the cache?
1531 cachestart = offset - o
1535 cachestart = offset - o
1532 cacheend = cachestart + length
1536 cacheend = cachestart + length
1533 if cachestart >= 0 and cacheend <= l:
1537 if cachestart >= 0 and cacheend <= l:
1534 if cachestart == 0 and cacheend == l:
1538 if cachestart == 0 and cacheend == l:
1535 return d # avoid a copy
1539 return d # avoid a copy
1536 return util.buffer(d, cachestart, cacheend - cachestart)
1540 return util.buffer(d, cachestart, cacheend - cachestart)
1537
1541
1538 return self._readsegment(offset, length, df=df)
1542 return self._readsegment(offset, length, df=df)
1539
1543
1540 def _getsegmentforrevs(self, startrev, endrev, df=None):
1544 def _getsegmentforrevs(self, startrev, endrev, df=None):
1541 """Obtain a segment of raw data corresponding to a range of revisions.
1545 """Obtain a segment of raw data corresponding to a range of revisions.
1542
1546
1543 Accepts the start and end revisions and an optional already-open
1547 Accepts the start and end revisions and an optional already-open
1544 file handle to be used for reading. If the file handle is read, its
1548 file handle to be used for reading. If the file handle is read, its
1545 seek position will not be preserved.
1549 seek position will not be preserved.
1546
1550
1547 Requests for data may be satisfied by a cache.
1551 Requests for data may be satisfied by a cache.
1548
1552
1549 Returns a 2-tuple of (offset, data) for the requested range of
1553 Returns a 2-tuple of (offset, data) for the requested range of
1550 revisions. Offset is the integer offset from the beginning of the
1554 revisions. Offset is the integer offset from the beginning of the
1551 revlog and data is a str or buffer of the raw byte data.
1555 revlog and data is a str or buffer of the raw byte data.
1552
1556
1553 Callers will need to call ``self.start(rev)`` and ``self.length(rev)``
1557 Callers will need to call ``self.start(rev)`` and ``self.length(rev)``
1554 to determine where each revision's data begins and ends.
1558 to determine where each revision's data begins and ends.
1555 """
1559 """
1556 # Inlined self.start(startrev) & self.end(endrev) for perf reasons
1560 # Inlined self.start(startrev) & self.end(endrev) for perf reasons
1557 # (functions are expensive).
1561 # (functions are expensive).
1558 index = self.index
1562 index = self.index
1559 istart = index[startrev]
1563 istart = index[startrev]
1560 start = int(istart[0] >> 16)
1564 start = int(istart[0] >> 16)
1561 if startrev == endrev:
1565 if startrev == endrev:
1562 end = start + istart[1]
1566 end = start + istart[1]
1563 else:
1567 else:
1564 iend = index[endrev]
1568 iend = index[endrev]
1565 end = int(iend[0] >> 16) + iend[1]
1569 end = int(iend[0] >> 16) + iend[1]
1566
1570
1567 if self._inline:
1571 if self._inline:
1568 start += (startrev + 1) * self._io.size
1572 start += (startrev + 1) * self._io.size
1569 end += (endrev + 1) * self._io.size
1573 end += (endrev + 1) * self._io.size
1570 length = end - start
1574 length = end - start
1571
1575
1572 return start, self._getsegment(start, length, df=df)
1576 return start, self._getsegment(start, length, df=df)
1573
1577
1574 def _chunk(self, rev, df=None):
1578 def _chunk(self, rev, df=None):
1575 """Obtain a single decompressed chunk for a revision.
1579 """Obtain a single decompressed chunk for a revision.
1576
1580
1577 Accepts an integer revision and an optional already-open file handle
1581 Accepts an integer revision and an optional already-open file handle
1578 to be used for reading. If used, the seek position of the file will not
1582 to be used for reading. If used, the seek position of the file will not
1579 be preserved.
1583 be preserved.
1580
1584
1581 Returns a str holding uncompressed data for the requested revision.
1585 Returns a str holding uncompressed data for the requested revision.
1582 """
1586 """
1583 return self.decompress(self._getsegmentforrevs(rev, rev, df=df)[1])
1587 return self.decompress(self._getsegmentforrevs(rev, rev, df=df)[1])
1584
1588
1585 def _chunks(self, revs, df=None, targetsize=None):
1589 def _chunks(self, revs, df=None, targetsize=None):
1586 """Obtain decompressed chunks for the specified revisions.
1590 """Obtain decompressed chunks for the specified revisions.
1587
1591
1588 Accepts an iterable of numeric revisions that are assumed to be in
1592 Accepts an iterable of numeric revisions that are assumed to be in
1589 ascending order. Also accepts an optional already-open file handle
1593 ascending order. Also accepts an optional already-open file handle
1590 to be used for reading. If used, the seek position of the file will
1594 to be used for reading. If used, the seek position of the file will
1591 not be preserved.
1595 not be preserved.
1592
1596
1593 This function is similar to calling ``self._chunk()`` multiple times,
1597 This function is similar to calling ``self._chunk()`` multiple times,
1594 but is faster.
1598 but is faster.
1595
1599
1596 Returns a list with decompressed data for each requested revision.
1600 Returns a list with decompressed data for each requested revision.
1597 """
1601 """
1598 if not revs:
1602 if not revs:
1599 return []
1603 return []
1600 start = self.start
1604 start = self.start
1601 length = self.length
1605 length = self.length
1602 inline = self._inline
1606 inline = self._inline
1603 iosize = self._io.size
1607 iosize = self._io.size
1604 buffer = util.buffer
1608 buffer = util.buffer
1605
1609
1606 l = []
1610 l = []
1607 ladd = l.append
1611 ladd = l.append
1608
1612
1609 if not self._withsparseread:
1613 if not self._withsparseread:
1610 slicedchunks = (revs,)
1614 slicedchunks = (revs,)
1611 else:
1615 else:
1612 slicedchunks = deltautil.slicechunk(
1616 slicedchunks = deltautil.slicechunk(
1613 self, revs, targetsize=targetsize
1617 self, revs, targetsize=targetsize
1614 )
1618 )
1615
1619
1616 for revschunk in slicedchunks:
1620 for revschunk in slicedchunks:
1617 firstrev = revschunk[0]
1621 firstrev = revschunk[0]
1618 # Skip trailing revisions with empty diff
1622 # Skip trailing revisions with empty diff
1619 for lastrev in revschunk[::-1]:
1623 for lastrev in revschunk[::-1]:
1620 if length(lastrev) != 0:
1624 if length(lastrev) != 0:
1621 break
1625 break
1622
1626
1623 try:
1627 try:
1624 offset, data = self._getsegmentforrevs(firstrev, lastrev, df=df)
1628 offset, data = self._getsegmentforrevs(firstrev, lastrev, df=df)
1625 except OverflowError:
1629 except OverflowError:
1626 # issue4215 - we can't cache a run of chunks greater than
1630 # issue4215 - we can't cache a run of chunks greater than
1627 # 2G on Windows
1631 # 2G on Windows
1628 return [self._chunk(rev, df=df) for rev in revschunk]
1632 return [self._chunk(rev, df=df) for rev in revschunk]
1629
1633
1630 decomp = self.decompress
1634 decomp = self.decompress
1631 for rev in revschunk:
1635 for rev in revschunk:
1632 chunkstart = start(rev)
1636 chunkstart = start(rev)
1633 if inline:
1637 if inline:
1634 chunkstart += (rev + 1) * iosize
1638 chunkstart += (rev + 1) * iosize
1635 chunklength = length(rev)
1639 chunklength = length(rev)
1636 ladd(decomp(buffer(data, chunkstart - offset, chunklength)))
1640 ladd(decomp(buffer(data, chunkstart - offset, chunklength)))
1637
1641
1638 return l
1642 return l
1639
1643
1640 def _chunkclear(self):
1644 def _chunkclear(self):
1641 """Clear the raw chunk cache."""
1645 """Clear the raw chunk cache."""
1642 self._chunkcache = (0, b'')
1646 self._chunkcache = (0, b'')
1643
1647
1644 def deltaparent(self, rev):
1648 def deltaparent(self, rev):
1645 """return deltaparent of the given revision"""
1649 """return deltaparent of the given revision"""
1646 base = self.index[rev][3]
1650 base = self.index[rev][3]
1647 if base == rev:
1651 if base == rev:
1648 return nullrev
1652 return nullrev
1649 elif self._generaldelta:
1653 elif self._generaldelta:
1650 return base
1654 return base
1651 else:
1655 else:
1652 return rev - 1
1656 return rev - 1
1653
1657
1654 def issnapshot(self, rev):
1658 def issnapshot(self, rev):
1655 """tells whether rev is a snapshot
1659 """tells whether rev is a snapshot
1656 """
1660 """
1657 if not self._sparserevlog:
1661 if not self._sparserevlog:
1658 return self.deltaparent(rev) == nullrev
1662 return self.deltaparent(rev) == nullrev
1659 elif util.safehasattr(self.index, b'issnapshot'):
1663 elif util.safehasattr(self.index, b'issnapshot'):
1660 # directly assign the method to cache the testing and access
1664 # directly assign the method to cache the testing and access
1661 self.issnapshot = self.index.issnapshot
1665 self.issnapshot = self.index.issnapshot
1662 return self.issnapshot(rev)
1666 return self.issnapshot(rev)
1663 if rev == nullrev:
1667 if rev == nullrev:
1664 return True
1668 return True
1665 entry = self.index[rev]
1669 entry = self.index[rev]
1666 base = entry[3]
1670 base = entry[3]
1667 if base == rev:
1671 if base == rev:
1668 return True
1672 return True
1669 if base == nullrev:
1673 if base == nullrev:
1670 return True
1674 return True
1671 p1 = entry[5]
1675 p1 = entry[5]
1672 p2 = entry[6]
1676 p2 = entry[6]
1673 if base == p1 or base == p2:
1677 if base == p1 or base == p2:
1674 return False
1678 return False
1675 return self.issnapshot(base)
1679 return self.issnapshot(base)
1676
1680
1677 def snapshotdepth(self, rev):
1681 def snapshotdepth(self, rev):
1678 """number of snapshot in the chain before this one"""
1682 """number of snapshot in the chain before this one"""
1679 if not self.issnapshot(rev):
1683 if not self.issnapshot(rev):
1680 raise error.ProgrammingError(b'revision %d not a snapshot')
1684 raise error.ProgrammingError(b'revision %d not a snapshot')
1681 return len(self._deltachain(rev)[0]) - 1
1685 return len(self._deltachain(rev)[0]) - 1
1682
1686
1683 def revdiff(self, rev1, rev2):
1687 def revdiff(self, rev1, rev2):
1684 """return or calculate a delta between two revisions
1688 """return or calculate a delta between two revisions
1685
1689
1686 The delta calculated is in binary form and is intended to be written to
1690 The delta calculated is in binary form and is intended to be written to
1687 revlog data directly. So this function needs raw revision data.
1691 revlog data directly. So this function needs raw revision data.
1688 """
1692 """
1689 if rev1 != nullrev and self.deltaparent(rev2) == rev1:
1693 if rev1 != nullrev and self.deltaparent(rev2) == rev1:
1690 return bytes(self._chunk(rev2))
1694 return bytes(self._chunk(rev2))
1691
1695
1692 return mdiff.textdiff(self.rawdata(rev1), self.rawdata(rev2))
1696 return mdiff.textdiff(self.rawdata(rev1), self.rawdata(rev2))
1693
1697
1694 def _processflags(self, text, flags, operation, raw=False):
1698 def _processflags(self, text, flags, operation, raw=False):
1695 """deprecated entry point to access flag processors"""
1699 """deprecated entry point to access flag processors"""
1696 msg = b'_processflag(...) use the specialized variant'
1700 msg = b'_processflag(...) use the specialized variant'
1697 util.nouideprecwarn(msg, b'5.2', stacklevel=2)
1701 util.nouideprecwarn(msg, b'5.2', stacklevel=2)
1698 if raw:
1702 if raw:
1699 return text, flagutil.processflagsraw(self, text, flags)
1703 return text, flagutil.processflagsraw(self, text, flags)
1700 elif operation == b'read':
1704 elif operation == b'read':
1701 return flagutil.processflagsread(self, text, flags)
1705 return flagutil.processflagsread(self, text, flags)
1702 else: # write operation
1706 else: # write operation
1703 return flagutil.processflagswrite(self, text, flags)
1707 return flagutil.processflagswrite(self, text, flags)
1704
1708
1705 def revision(self, nodeorrev, _df=None, raw=False):
1709 def revision(self, nodeorrev, _df=None, raw=False):
1706 """return an uncompressed revision of a given node or revision
1710 """return an uncompressed revision of a given node or revision
1707 number.
1711 number.
1708
1712
1709 _df - an existing file handle to read from. (internal-only)
1713 _df - an existing file handle to read from. (internal-only)
1710 raw - an optional argument specifying if the revision data is to be
1714 raw - an optional argument specifying if the revision data is to be
1711 treated as raw data when applying flag transforms. 'raw' should be set
1715 treated as raw data when applying flag transforms. 'raw' should be set
1712 to True when generating changegroups or in debug commands.
1716 to True when generating changegroups or in debug commands.
1713 """
1717 """
1714 if raw:
1718 if raw:
1715 msg = (
1719 msg = (
1716 b'revlog.revision(..., raw=True) is deprecated, '
1720 b'revlog.revision(..., raw=True) is deprecated, '
1717 b'use revlog.rawdata(...)'
1721 b'use revlog.rawdata(...)'
1718 )
1722 )
1719 util.nouideprecwarn(msg, b'5.2', stacklevel=2)
1723 util.nouideprecwarn(msg, b'5.2', stacklevel=2)
1720 return self._revisiondata(nodeorrev, _df, raw=raw)[0]
1724 return self._revisiondata(nodeorrev, _df, raw=raw)[0]
1721
1725
1722 def sidedata(self, nodeorrev, _df=None):
1726 def sidedata(self, nodeorrev, _df=None):
1723 """a map of extra data related to the changeset but not part of the hash
1727 """a map of extra data related to the changeset but not part of the hash
1724
1728
1725 This function currently return a dictionary. However, more advanced
1729 This function currently return a dictionary. However, more advanced
1726 mapping object will likely be used in the future for a more
1730 mapping object will likely be used in the future for a more
1727 efficient/lazy code.
1731 efficient/lazy code.
1728 """
1732 """
1729 return self._revisiondata(nodeorrev, _df)[1]
1733 return self._revisiondata(nodeorrev, _df)[1]
1730
1734
1731 def _revisiondata(self, nodeorrev, _df=None, raw=False):
1735 def _revisiondata(self, nodeorrev, _df=None, raw=False):
1732 # deal with <nodeorrev> argument type
1736 # deal with <nodeorrev> argument type
1733 if isinstance(nodeorrev, int):
1737 if isinstance(nodeorrev, int):
1734 rev = nodeorrev
1738 rev = nodeorrev
1735 node = self.node(rev)
1739 node = self.node(rev)
1736 else:
1740 else:
1737 node = nodeorrev
1741 node = nodeorrev
1738 rev = None
1742 rev = None
1739
1743
1740 # fast path the special `nullid` rev
1744 # fast path the special `nullid` rev
1741 if node == nullid:
1745 if node == nullid:
1742 return b"", {}
1746 return b"", {}
1743
1747
1744 # The text as stored inside the revlog. Might be the revision or might
1748 # The text as stored inside the revlog. Might be the revision or might
1745 # need to be processed to retrieve the revision.
1749 # need to be processed to retrieve the revision.
1746 rawtext = None
1750 rawtext = None
1747
1751
1748 rev, rawtext, validated = self._rawtext(node, rev, _df=_df)
1752 rev, rawtext, validated = self._rawtext(node, rev, _df=_df)
1749
1753
1750 if raw and validated:
1754 if raw and validated:
1751 # if we don't want to process the raw text and that raw
1755 # if we don't want to process the raw text and that raw
1752 # text is cached, we can exit early.
1756 # text is cached, we can exit early.
1753 return rawtext, {}
1757 return rawtext, {}
1754 if rev is None:
1758 if rev is None:
1755 rev = self.rev(node)
1759 rev = self.rev(node)
1756 # the revlog's flag for this revision
1760 # the revlog's flag for this revision
1757 # (usually alter its state or content)
1761 # (usually alter its state or content)
1758 flags = self.flags(rev)
1762 flags = self.flags(rev)
1759
1763
1760 if validated and flags == REVIDX_DEFAULT_FLAGS:
1764 if validated and flags == REVIDX_DEFAULT_FLAGS:
1761 # no extra flags set, no flag processor runs, text = rawtext
1765 # no extra flags set, no flag processor runs, text = rawtext
1762 return rawtext, {}
1766 return rawtext, {}
1763
1767
1764 sidedata = {}
1768 sidedata = {}
1765 if raw:
1769 if raw:
1766 validatehash = flagutil.processflagsraw(self, rawtext, flags)
1770 validatehash = flagutil.processflagsraw(self, rawtext, flags)
1767 text = rawtext
1771 text = rawtext
1768 else:
1772 else:
1769 try:
1773 try:
1770 r = flagutil.processflagsread(self, rawtext, flags)
1774 r = flagutil.processflagsread(self, rawtext, flags)
1771 except error.SidedataHashError as exc:
1775 except error.SidedataHashError as exc:
1772 msg = _(b"integrity check failed on %s:%s sidedata key %d")
1776 msg = _(b"integrity check failed on %s:%s sidedata key %d")
1773 msg %= (self.indexfile, pycompat.bytestr(rev), exc.sidedatakey)
1777 msg %= (self.indexfile, pycompat.bytestr(rev), exc.sidedatakey)
1774 raise error.RevlogError(msg)
1778 raise error.RevlogError(msg)
1775 text, validatehash, sidedata = r
1779 text, validatehash, sidedata = r
1776 if validatehash:
1780 if validatehash:
1777 self.checkhash(text, node, rev=rev)
1781 self.checkhash(text, node, rev=rev)
1778 if not validated:
1782 if not validated:
1779 self._revisioncache = (node, rev, rawtext)
1783 self._revisioncache = (node, rev, rawtext)
1780
1784
1781 return text, sidedata
1785 return text, sidedata
1782
1786
1783 def _rawtext(self, node, rev, _df=None):
1787 def _rawtext(self, node, rev, _df=None):
1784 """return the possibly unvalidated rawtext for a revision
1788 """return the possibly unvalidated rawtext for a revision
1785
1789
1786 returns (rev, rawtext, validated)
1790 returns (rev, rawtext, validated)
1787 """
1791 """
1788
1792
1789 # revision in the cache (could be useful to apply delta)
1793 # revision in the cache (could be useful to apply delta)
1790 cachedrev = None
1794 cachedrev = None
1791 # An intermediate text to apply deltas to
1795 # An intermediate text to apply deltas to
1792 basetext = None
1796 basetext = None
1793
1797
1794 # Check if we have the entry in cache
1798 # Check if we have the entry in cache
1795 # The cache entry looks like (node, rev, rawtext)
1799 # The cache entry looks like (node, rev, rawtext)
1796 if self._revisioncache:
1800 if self._revisioncache:
1797 if self._revisioncache[0] == node:
1801 if self._revisioncache[0] == node:
1798 return (rev, self._revisioncache[2], True)
1802 return (rev, self._revisioncache[2], True)
1799 cachedrev = self._revisioncache[1]
1803 cachedrev = self._revisioncache[1]
1800
1804
1801 if rev is None:
1805 if rev is None:
1802 rev = self.rev(node)
1806 rev = self.rev(node)
1803
1807
1804 chain, stopped = self._deltachain(rev, stoprev=cachedrev)
1808 chain, stopped = self._deltachain(rev, stoprev=cachedrev)
1805 if stopped:
1809 if stopped:
1806 basetext = self._revisioncache[2]
1810 basetext = self._revisioncache[2]
1807
1811
1808 # drop cache to save memory, the caller is expected to
1812 # drop cache to save memory, the caller is expected to
1809 # update self._revisioncache after validating the text
1813 # update self._revisioncache after validating the text
1810 self._revisioncache = None
1814 self._revisioncache = None
1811
1815
1812 targetsize = None
1816 targetsize = None
1813 rawsize = self.index[rev][2]
1817 rawsize = self.index[rev][2]
1814 if 0 <= rawsize:
1818 if 0 <= rawsize:
1815 targetsize = 4 * rawsize
1819 targetsize = 4 * rawsize
1816
1820
1817 bins = self._chunks(chain, df=_df, targetsize=targetsize)
1821 bins = self._chunks(chain, df=_df, targetsize=targetsize)
1818 if basetext is None:
1822 if basetext is None:
1819 basetext = bytes(bins[0])
1823 basetext = bytes(bins[0])
1820 bins = bins[1:]
1824 bins = bins[1:]
1821
1825
1822 rawtext = mdiff.patches(basetext, bins)
1826 rawtext = mdiff.patches(basetext, bins)
1823 del basetext # let us have a chance to free memory early
1827 del basetext # let us have a chance to free memory early
1824 return (rev, rawtext, False)
1828 return (rev, rawtext, False)
1825
1829
1826 def rawdata(self, nodeorrev, _df=None):
1830 def rawdata(self, nodeorrev, _df=None):
1827 """return an uncompressed raw data of a given node or revision number.
1831 """return an uncompressed raw data of a given node or revision number.
1828
1832
1829 _df - an existing file handle to read from. (internal-only)
1833 _df - an existing file handle to read from. (internal-only)
1830 """
1834 """
1831 return self._revisiondata(nodeorrev, _df, raw=True)[0]
1835 return self._revisiondata(nodeorrev, _df, raw=True)[0]
1832
1836
1833 def hash(self, text, p1, p2):
1837 def hash(self, text, p1, p2):
1834 """Compute a node hash.
1838 """Compute a node hash.
1835
1839
1836 Available as a function so that subclasses can replace the hash
1840 Available as a function so that subclasses can replace the hash
1837 as needed.
1841 as needed.
1838 """
1842 """
1839 return storageutil.hashrevisionsha1(text, p1, p2)
1843 return storageutil.hashrevisionsha1(text, p1, p2)
1840
1844
1841 def checkhash(self, text, node, p1=None, p2=None, rev=None):
1845 def checkhash(self, text, node, p1=None, p2=None, rev=None):
1842 """Check node hash integrity.
1846 """Check node hash integrity.
1843
1847
1844 Available as a function so that subclasses can extend hash mismatch
1848 Available as a function so that subclasses can extend hash mismatch
1845 behaviors as needed.
1849 behaviors as needed.
1846 """
1850 """
1847 try:
1851 try:
1848 if p1 is None and p2 is None:
1852 if p1 is None and p2 is None:
1849 p1, p2 = self.parents(node)
1853 p1, p2 = self.parents(node)
1850 if node != self.hash(text, p1, p2):
1854 if node != self.hash(text, p1, p2):
1851 # Clear the revision cache on hash failure. The revision cache
1855 # Clear the revision cache on hash failure. The revision cache
1852 # only stores the raw revision and clearing the cache does have
1856 # only stores the raw revision and clearing the cache does have
1853 # the side-effect that we won't have a cache hit when the raw
1857 # the side-effect that we won't have a cache hit when the raw
1854 # revision data is accessed. But this case should be rare and
1858 # revision data is accessed. But this case should be rare and
1855 # it is extra work to teach the cache about the hash
1859 # it is extra work to teach the cache about the hash
1856 # verification state.
1860 # verification state.
1857 if self._revisioncache and self._revisioncache[0] == node:
1861 if self._revisioncache and self._revisioncache[0] == node:
1858 self._revisioncache = None
1862 self._revisioncache = None
1859
1863
1860 revornode = rev
1864 revornode = rev
1861 if revornode is None:
1865 if revornode is None:
1862 revornode = templatefilters.short(hex(node))
1866 revornode = templatefilters.short(hex(node))
1863 raise error.RevlogError(
1867 raise error.RevlogError(
1864 _(b"integrity check failed on %s:%s")
1868 _(b"integrity check failed on %s:%s")
1865 % (self.indexfile, pycompat.bytestr(revornode))
1869 % (self.indexfile, pycompat.bytestr(revornode))
1866 )
1870 )
1867 except error.RevlogError:
1871 except error.RevlogError:
1868 if self._censorable and storageutil.iscensoredtext(text):
1872 if self._censorable and storageutil.iscensoredtext(text):
1869 raise error.CensoredNodeError(self.indexfile, node, text)
1873 raise error.CensoredNodeError(self.indexfile, node, text)
1870 raise
1874 raise
1871
1875
1872 def _enforceinlinesize(self, tr, fp=None):
1876 def _enforceinlinesize(self, tr, fp=None):
1873 """Check if the revlog is too big for inline and convert if so.
1877 """Check if the revlog is too big for inline and convert if so.
1874
1878
1875 This should be called after revisions are added to the revlog. If the
1879 This should be called after revisions are added to the revlog. If the
1876 revlog has grown too large to be an inline revlog, it will convert it
1880 revlog has grown too large to be an inline revlog, it will convert it
1877 to use multiple index and data files.
1881 to use multiple index and data files.
1878 """
1882 """
1879 tiprev = len(self) - 1
1883 tiprev = len(self) - 1
1880 if (
1884 if (
1881 not self._inline
1885 not self._inline
1882 or (self.start(tiprev) + self.length(tiprev)) < _maxinline
1886 or (self.start(tiprev) + self.length(tiprev)) < _maxinline
1883 ):
1887 ):
1884 return
1888 return
1885
1889
1886 trinfo = tr.find(self.indexfile)
1890 trinfo = tr.find(self.indexfile)
1887 if trinfo is None:
1891 if trinfo is None:
1888 raise error.RevlogError(
1892 raise error.RevlogError(
1889 _(b"%s not found in the transaction") % self.indexfile
1893 _(b"%s not found in the transaction") % self.indexfile
1890 )
1894 )
1891
1895
1892 trindex = trinfo[2]
1896 trindex = trinfo[2]
1893 if trindex is not None:
1897 if trindex is not None:
1894 dataoff = self.start(trindex)
1898 dataoff = self.start(trindex)
1895 else:
1899 else:
1896 # revlog was stripped at start of transaction, use all leftover data
1900 # revlog was stripped at start of transaction, use all leftover data
1897 trindex = len(self) - 1
1901 trindex = len(self) - 1
1898 dataoff = self.end(tiprev)
1902 dataoff = self.end(tiprev)
1899
1903
1900 tr.add(self.datafile, dataoff)
1904 tr.add(self.datafile, dataoff)
1901
1905
1902 if fp:
1906 if fp:
1903 fp.flush()
1907 fp.flush()
1904 fp.close()
1908 fp.close()
1905 # We can't use the cached file handle after close(). So prevent
1909 # We can't use the cached file handle after close(). So prevent
1906 # its usage.
1910 # its usage.
1907 self._writinghandles = None
1911 self._writinghandles = None
1908
1912
1909 with self._indexfp(b'r') as ifh, self._datafp(b'w') as dfh:
1913 with self._indexfp(b'r') as ifh, self._datafp(b'w') as dfh:
1910 for r in self:
1914 for r in self:
1911 dfh.write(self._getsegmentforrevs(r, r, df=ifh)[1])
1915 dfh.write(self._getsegmentforrevs(r, r, df=ifh)[1])
1912
1916
1913 with self._indexfp(b'w') as fp:
1917 with self._indexfp(b'w') as fp:
1914 self.version &= ~FLAG_INLINE_DATA
1918 self.version &= ~FLAG_INLINE_DATA
1915 self._inline = False
1919 self._inline = False
1916 io = self._io
1920 io = self._io
1917 for i in self:
1921 for i in self:
1918 e = io.packentry(self.index[i], self.node, self.version, i)
1922 e = io.packentry(self.index[i], self.node, self.version, i)
1919 fp.write(e)
1923 fp.write(e)
1920
1924
1921 # the temp file replace the real index when we exit the context
1925 # the temp file replace the real index when we exit the context
1922 # manager
1926 # manager
1923
1927
1924 tr.replace(self.indexfile, trindex * self._io.size)
1928 tr.replace(self.indexfile, trindex * self._io.size)
1925 self._chunkclear()
1929 self._chunkclear()
1926
1930
1927 def _nodeduplicatecallback(self, transaction, node):
1931 def _nodeduplicatecallback(self, transaction, node):
1928 """called when trying to add a node already stored.
1932 """called when trying to add a node already stored.
1929 """
1933 """
1930
1934
1931 def addrevision(
1935 def addrevision(
1932 self,
1936 self,
1933 text,
1937 text,
1934 transaction,
1938 transaction,
1935 link,
1939 link,
1936 p1,
1940 p1,
1937 p2,
1941 p2,
1938 cachedelta=None,
1942 cachedelta=None,
1939 node=None,
1943 node=None,
1940 flags=REVIDX_DEFAULT_FLAGS,
1944 flags=REVIDX_DEFAULT_FLAGS,
1941 deltacomputer=None,
1945 deltacomputer=None,
1942 sidedata=None,
1946 sidedata=None,
1943 ):
1947 ):
1944 """add a revision to the log
1948 """add a revision to the log
1945
1949
1946 text - the revision data to add
1950 text - the revision data to add
1947 transaction - the transaction object used for rollback
1951 transaction - the transaction object used for rollback
1948 link - the linkrev data to add
1952 link - the linkrev data to add
1949 p1, p2 - the parent nodeids of the revision
1953 p1, p2 - the parent nodeids of the revision
1950 cachedelta - an optional precomputed delta
1954 cachedelta - an optional precomputed delta
1951 node - nodeid of revision; typically node is not specified, and it is
1955 node - nodeid of revision; typically node is not specified, and it is
1952 computed by default as hash(text, p1, p2), however subclasses might
1956 computed by default as hash(text, p1, p2), however subclasses might
1953 use different hashing method (and override checkhash() in such case)
1957 use different hashing method (and override checkhash() in such case)
1954 flags - the known flags to set on the revision
1958 flags - the known flags to set on the revision
1955 deltacomputer - an optional deltacomputer instance shared between
1959 deltacomputer - an optional deltacomputer instance shared between
1956 multiple calls
1960 multiple calls
1957 """
1961 """
1958 if link == nullrev:
1962 if link == nullrev:
1959 raise error.RevlogError(
1963 raise error.RevlogError(
1960 _(b"attempted to add linkrev -1 to %s") % self.indexfile
1964 _(b"attempted to add linkrev -1 to %s") % self.indexfile
1961 )
1965 )
1962
1966
1963 if sidedata is None:
1967 if sidedata is None:
1964 sidedata = {}
1968 sidedata = {}
1965 flags = flags & ~REVIDX_SIDEDATA
1969 flags = flags & ~REVIDX_SIDEDATA
1966 elif not self.hassidedata:
1970 elif not self.hassidedata:
1967 raise error.ProgrammingError(
1971 raise error.ProgrammingError(
1968 _(b"trying to add sidedata to a revlog who don't support them")
1972 _(b"trying to add sidedata to a revlog who don't support them")
1969 )
1973 )
1970 else:
1974 else:
1971 flags |= REVIDX_SIDEDATA
1975 flags |= REVIDX_SIDEDATA
1972
1976
1973 if flags:
1977 if flags:
1974 node = node or self.hash(text, p1, p2)
1978 node = node or self.hash(text, p1, p2)
1975
1979
1976 rawtext, validatehash = flagutil.processflagswrite(
1980 rawtext, validatehash = flagutil.processflagswrite(
1977 self, text, flags, sidedata=sidedata
1981 self, text, flags, sidedata=sidedata
1978 )
1982 )
1979
1983
1980 # If the flag processor modifies the revision data, ignore any provided
1984 # If the flag processor modifies the revision data, ignore any provided
1981 # cachedelta.
1985 # cachedelta.
1982 if rawtext != text:
1986 if rawtext != text:
1983 cachedelta = None
1987 cachedelta = None
1984
1988
1985 if len(rawtext) > _maxentrysize:
1989 if len(rawtext) > _maxentrysize:
1986 raise error.RevlogError(
1990 raise error.RevlogError(
1987 _(
1991 _(
1988 b"%s: size of %d bytes exceeds maximum revlog storage of 2GiB"
1992 b"%s: size of %d bytes exceeds maximum revlog storage of 2GiB"
1989 )
1993 )
1990 % (self.indexfile, len(rawtext))
1994 % (self.indexfile, len(rawtext))
1991 )
1995 )
1992
1996
1993 node = node or self.hash(rawtext, p1, p2)
1997 node = node or self.hash(rawtext, p1, p2)
1994 if node in self.nodemap:
1998 if node in self.nodemap:
1995 return node
1999 return node
1996
2000
1997 if validatehash:
2001 if validatehash:
1998 self.checkhash(rawtext, node, p1=p1, p2=p2)
2002 self.checkhash(rawtext, node, p1=p1, p2=p2)
1999
2003
2000 return self.addrawrevision(
2004 return self.addrawrevision(
2001 rawtext,
2005 rawtext,
2002 transaction,
2006 transaction,
2003 link,
2007 link,
2004 p1,
2008 p1,
2005 p2,
2009 p2,
2006 node,
2010 node,
2007 flags,
2011 flags,
2008 cachedelta=cachedelta,
2012 cachedelta=cachedelta,
2009 deltacomputer=deltacomputer,
2013 deltacomputer=deltacomputer,
2010 )
2014 )
2011
2015
2012 def addrawrevision(
2016 def addrawrevision(
2013 self,
2017 self,
2014 rawtext,
2018 rawtext,
2015 transaction,
2019 transaction,
2016 link,
2020 link,
2017 p1,
2021 p1,
2018 p2,
2022 p2,
2019 node,
2023 node,
2020 flags,
2024 flags,
2021 cachedelta=None,
2025 cachedelta=None,
2022 deltacomputer=None,
2026 deltacomputer=None,
2023 ):
2027 ):
2024 """add a raw revision with known flags, node and parents
2028 """add a raw revision with known flags, node and parents
2025 useful when reusing a revision not stored in this revlog (ex: received
2029 useful when reusing a revision not stored in this revlog (ex: received
2026 over wire, or read from an external bundle).
2030 over wire, or read from an external bundle).
2027 """
2031 """
2028 dfh = None
2032 dfh = None
2029 if not self._inline:
2033 if not self._inline:
2030 dfh = self._datafp(b"a+")
2034 dfh = self._datafp(b"a+")
2031 ifh = self._indexfp(b"a+")
2035 ifh = self._indexfp(b"a+")
2032 try:
2036 try:
2033 return self._addrevision(
2037 return self._addrevision(
2034 node,
2038 node,
2035 rawtext,
2039 rawtext,
2036 transaction,
2040 transaction,
2037 link,
2041 link,
2038 p1,
2042 p1,
2039 p2,
2043 p2,
2040 flags,
2044 flags,
2041 cachedelta,
2045 cachedelta,
2042 ifh,
2046 ifh,
2043 dfh,
2047 dfh,
2044 deltacomputer=deltacomputer,
2048 deltacomputer=deltacomputer,
2045 )
2049 )
2046 finally:
2050 finally:
2047 if dfh:
2051 if dfh:
2048 dfh.close()
2052 dfh.close()
2049 ifh.close()
2053 ifh.close()
2050
2054
2051 def compress(self, data):
2055 def compress(self, data):
2052 """Generate a possibly-compressed representation of data."""
2056 """Generate a possibly-compressed representation of data."""
2053 if not data:
2057 if not data:
2054 return b'', data
2058 return b'', data
2055
2059
2056 compressed = self._compressor.compress(data)
2060 compressed = self._compressor.compress(data)
2057
2061
2058 if compressed:
2062 if compressed:
2059 # The revlog compressor added the header in the returned data.
2063 # The revlog compressor added the header in the returned data.
2060 return b'', compressed
2064 return b'', compressed
2061
2065
2062 if data[0:1] == b'\0':
2066 if data[0:1] == b'\0':
2063 return b'', data
2067 return b'', data
2064 return b'u', data
2068 return b'u', data
2065
2069
2066 def decompress(self, data):
2070 def decompress(self, data):
2067 """Decompress a revlog chunk.
2071 """Decompress a revlog chunk.
2068
2072
2069 The chunk is expected to begin with a header identifying the
2073 The chunk is expected to begin with a header identifying the
2070 format type so it can be routed to an appropriate decompressor.
2074 format type so it can be routed to an appropriate decompressor.
2071 """
2075 """
2072 if not data:
2076 if not data:
2073 return data
2077 return data
2074
2078
2075 # Revlogs are read much more frequently than they are written and many
2079 # Revlogs are read much more frequently than they are written and many
2076 # chunks only take microseconds to decompress, so performance is
2080 # chunks only take microseconds to decompress, so performance is
2077 # important here.
2081 # important here.
2078 #
2082 #
2079 # We can make a few assumptions about revlogs:
2083 # We can make a few assumptions about revlogs:
2080 #
2084 #
2081 # 1) the majority of chunks will be compressed (as opposed to inline
2085 # 1) the majority of chunks will be compressed (as opposed to inline
2082 # raw data).
2086 # raw data).
2083 # 2) decompressing *any* data will likely by at least 10x slower than
2087 # 2) decompressing *any* data will likely by at least 10x slower than
2084 # returning raw inline data.
2088 # returning raw inline data.
2085 # 3) we want to prioritize common and officially supported compression
2089 # 3) we want to prioritize common and officially supported compression
2086 # engines
2090 # engines
2087 #
2091 #
2088 # It follows that we want to optimize for "decompress compressed data
2092 # It follows that we want to optimize for "decompress compressed data
2089 # when encoded with common and officially supported compression engines"
2093 # when encoded with common and officially supported compression engines"
2090 # case over "raw data" and "data encoded by less common or non-official
2094 # case over "raw data" and "data encoded by less common or non-official
2091 # compression engines." That is why we have the inline lookup first
2095 # compression engines." That is why we have the inline lookup first
2092 # followed by the compengines lookup.
2096 # followed by the compengines lookup.
2093 #
2097 #
2094 # According to `hg perfrevlogchunks`, this is ~0.5% faster for zlib
2098 # According to `hg perfrevlogchunks`, this is ~0.5% faster for zlib
2095 # compressed chunks. And this matters for changelog and manifest reads.
2099 # compressed chunks. And this matters for changelog and manifest reads.
2096 t = data[0:1]
2100 t = data[0:1]
2097
2101
2098 if t == b'x':
2102 if t == b'x':
2099 try:
2103 try:
2100 return _zlibdecompress(data)
2104 return _zlibdecompress(data)
2101 except zlib.error as e:
2105 except zlib.error as e:
2102 raise error.RevlogError(
2106 raise error.RevlogError(
2103 _(b'revlog decompress error: %s')
2107 _(b'revlog decompress error: %s')
2104 % stringutil.forcebytestr(e)
2108 % stringutil.forcebytestr(e)
2105 )
2109 )
2106 # '\0' is more common than 'u' so it goes first.
2110 # '\0' is more common than 'u' so it goes first.
2107 elif t == b'\0':
2111 elif t == b'\0':
2108 return data
2112 return data
2109 elif t == b'u':
2113 elif t == b'u':
2110 return util.buffer(data, 1)
2114 return util.buffer(data, 1)
2111
2115
2112 try:
2116 try:
2113 compressor = self._decompressors[t]
2117 compressor = self._decompressors[t]
2114 except KeyError:
2118 except KeyError:
2115 try:
2119 try:
2116 engine = util.compengines.forrevlogheader(t)
2120 engine = util.compengines.forrevlogheader(t)
2117 compressor = engine.revlogcompressor(self._compengineopts)
2121 compressor = engine.revlogcompressor(self._compengineopts)
2118 self._decompressors[t] = compressor
2122 self._decompressors[t] = compressor
2119 except KeyError:
2123 except KeyError:
2120 raise error.RevlogError(_(b'unknown compression type %r') % t)
2124 raise error.RevlogError(_(b'unknown compression type %r') % t)
2121
2125
2122 return compressor.decompress(data)
2126 return compressor.decompress(data)
2123
2127
2124 def _addrevision(
2128 def _addrevision(
2125 self,
2129 self,
2126 node,
2130 node,
2127 rawtext,
2131 rawtext,
2128 transaction,
2132 transaction,
2129 link,
2133 link,
2130 p1,
2134 p1,
2131 p2,
2135 p2,
2132 flags,
2136 flags,
2133 cachedelta,
2137 cachedelta,
2134 ifh,
2138 ifh,
2135 dfh,
2139 dfh,
2136 alwayscache=False,
2140 alwayscache=False,
2137 deltacomputer=None,
2141 deltacomputer=None,
2138 ):
2142 ):
2139 """internal function to add revisions to the log
2143 """internal function to add revisions to the log
2140
2144
2141 see addrevision for argument descriptions.
2145 see addrevision for argument descriptions.
2142
2146
2143 note: "addrevision" takes non-raw text, "_addrevision" takes raw text.
2147 note: "addrevision" takes non-raw text, "_addrevision" takes raw text.
2144
2148
2145 if "deltacomputer" is not provided or None, a defaultdeltacomputer will
2149 if "deltacomputer" is not provided or None, a defaultdeltacomputer will
2146 be used.
2150 be used.
2147
2151
2148 invariants:
2152 invariants:
2149 - rawtext is optional (can be None); if not set, cachedelta must be set.
2153 - rawtext is optional (can be None); if not set, cachedelta must be set.
2150 if both are set, they must correspond to each other.
2154 if both are set, they must correspond to each other.
2151 """
2155 """
2152 if node == nullid:
2156 if node == nullid:
2153 raise error.RevlogError(
2157 raise error.RevlogError(
2154 _(b"%s: attempt to add null revision") % self.indexfile
2158 _(b"%s: attempt to add null revision") % self.indexfile
2155 )
2159 )
2156 if node == wdirid or node in wdirfilenodeids:
2160 if node == wdirid or node in wdirfilenodeids:
2157 raise error.RevlogError(
2161 raise error.RevlogError(
2158 _(b"%s: attempt to add wdir revision") % self.indexfile
2162 _(b"%s: attempt to add wdir revision") % self.indexfile
2159 )
2163 )
2160
2164
2161 if self._inline:
2165 if self._inline:
2162 fh = ifh
2166 fh = ifh
2163 else:
2167 else:
2164 fh = dfh
2168 fh = dfh
2165
2169
2166 btext = [rawtext]
2170 btext = [rawtext]
2167
2171
2168 curr = len(self)
2172 curr = len(self)
2169 prev = curr - 1
2173 prev = curr - 1
2170 offset = self.end(prev)
2174 offset = self.end(prev)
2171 p1r, p2r = self.rev(p1), self.rev(p2)
2175 p1r, p2r = self.rev(p1), self.rev(p2)
2172
2176
2173 # full versions are inserted when the needed deltas
2177 # full versions are inserted when the needed deltas
2174 # become comparable to the uncompressed text
2178 # become comparable to the uncompressed text
2175 if rawtext is None:
2179 if rawtext is None:
2176 # need rawtext size, before changed by flag processors, which is
2180 # need rawtext size, before changed by flag processors, which is
2177 # the non-raw size. use revlog explicitly to avoid filelog's extra
2181 # the non-raw size. use revlog explicitly to avoid filelog's extra
2178 # logic that might remove metadata size.
2182 # logic that might remove metadata size.
2179 textlen = mdiff.patchedsize(
2183 textlen = mdiff.patchedsize(
2180 revlog.size(self, cachedelta[0]), cachedelta[1]
2184 revlog.size(self, cachedelta[0]), cachedelta[1]
2181 )
2185 )
2182 else:
2186 else:
2183 textlen = len(rawtext)
2187 textlen = len(rawtext)
2184
2188
2185 if deltacomputer is None:
2189 if deltacomputer is None:
2186 deltacomputer = deltautil.deltacomputer(self)
2190 deltacomputer = deltautil.deltacomputer(self)
2187
2191
2188 revinfo = _revisioninfo(node, p1, p2, btext, textlen, cachedelta, flags)
2192 revinfo = _revisioninfo(node, p1, p2, btext, textlen, cachedelta, flags)
2189
2193
2190 deltainfo = deltacomputer.finddeltainfo(revinfo, fh)
2194 deltainfo = deltacomputer.finddeltainfo(revinfo, fh)
2191
2195
2192 e = (
2196 e = (
2193 offset_type(offset, flags),
2197 offset_type(offset, flags),
2194 deltainfo.deltalen,
2198 deltainfo.deltalen,
2195 textlen,
2199 textlen,
2196 deltainfo.base,
2200 deltainfo.base,
2197 link,
2201 link,
2198 p1r,
2202 p1r,
2199 p2r,
2203 p2r,
2200 node,
2204 node,
2201 )
2205 )
2202 self.index.append(e)
2206 self.index.append(e)
2203
2207
2204 # Reset the pure node cache start lookup offset to account for new
2208 # Reset the pure node cache start lookup offset to account for new
2205 # revision.
2209 # revision.
2206 if self._nodepos is not None:
2210 if self._nodepos is not None:
2207 self._nodepos = curr
2211 self._nodepos = curr
2208
2212
2209 entry = self._io.packentry(e, self.node, self.version, curr)
2213 entry = self._io.packentry(e, self.node, self.version, curr)
2210 self._writeentry(
2214 self._writeentry(
2211 transaction, ifh, dfh, entry, deltainfo.data, link, offset
2215 transaction, ifh, dfh, entry, deltainfo.data, link, offset
2212 )
2216 )
2213
2217
2214 rawtext = btext[0]
2218 rawtext = btext[0]
2215
2219
2216 if alwayscache and rawtext is None:
2220 if alwayscache and rawtext is None:
2217 rawtext = deltacomputer.buildtext(revinfo, fh)
2221 rawtext = deltacomputer.buildtext(revinfo, fh)
2218
2222
2219 if type(rawtext) == bytes: # only accept immutable objects
2223 if type(rawtext) == bytes: # only accept immutable objects
2220 self._revisioncache = (node, curr, rawtext)
2224 self._revisioncache = (node, curr, rawtext)
2221 self._chainbasecache[curr] = deltainfo.chainbase
2225 self._chainbasecache[curr] = deltainfo.chainbase
2222 return node
2226 return node
2223
2227
2224 def _writeentry(self, transaction, ifh, dfh, entry, data, link, offset):
2228 def _writeentry(self, transaction, ifh, dfh, entry, data, link, offset):
2225 # Files opened in a+ mode have inconsistent behavior on various
2229 # Files opened in a+ mode have inconsistent behavior on various
2226 # platforms. Windows requires that a file positioning call be made
2230 # platforms. Windows requires that a file positioning call be made
2227 # when the file handle transitions between reads and writes. See
2231 # when the file handle transitions between reads and writes. See
2228 # 3686fa2b8eee and the mixedfilemodewrapper in windows.py. On other
2232 # 3686fa2b8eee and the mixedfilemodewrapper in windows.py. On other
2229 # platforms, Python or the platform itself can be buggy. Some versions
2233 # platforms, Python or the platform itself can be buggy. Some versions
2230 # of Solaris have been observed to not append at the end of the file
2234 # of Solaris have been observed to not append at the end of the file
2231 # if the file was seeked to before the end. See issue4943 for more.
2235 # if the file was seeked to before the end. See issue4943 for more.
2232 #
2236 #
2233 # We work around this issue by inserting a seek() before writing.
2237 # We work around this issue by inserting a seek() before writing.
2234 # Note: This is likely not necessary on Python 3. However, because
2238 # Note: This is likely not necessary on Python 3. However, because
2235 # the file handle is reused for reads and may be seeked there, we need
2239 # the file handle is reused for reads and may be seeked there, we need
2236 # to be careful before changing this.
2240 # to be careful before changing this.
2237 ifh.seek(0, os.SEEK_END)
2241 ifh.seek(0, os.SEEK_END)
2238 if dfh:
2242 if dfh:
2239 dfh.seek(0, os.SEEK_END)
2243 dfh.seek(0, os.SEEK_END)
2240
2244
2241 curr = len(self) - 1
2245 curr = len(self) - 1
2242 if not self._inline:
2246 if not self._inline:
2243 transaction.add(self.datafile, offset)
2247 transaction.add(self.datafile, offset)
2244 transaction.add(self.indexfile, curr * len(entry))
2248 transaction.add(self.indexfile, curr * len(entry))
2245 if data[0]:
2249 if data[0]:
2246 dfh.write(data[0])
2250 dfh.write(data[0])
2247 dfh.write(data[1])
2251 dfh.write(data[1])
2248 ifh.write(entry)
2252 ifh.write(entry)
2249 else:
2253 else:
2250 offset += curr * self._io.size
2254 offset += curr * self._io.size
2251 transaction.add(self.indexfile, offset, curr)
2255 transaction.add(self.indexfile, offset, curr)
2252 ifh.write(entry)
2256 ifh.write(entry)
2253 ifh.write(data[0])
2257 ifh.write(data[0])
2254 ifh.write(data[1])
2258 ifh.write(data[1])
2255 self._enforceinlinesize(transaction, ifh)
2259 self._enforceinlinesize(transaction, ifh)
2256
2260
2257 def addgroup(self, deltas, linkmapper, transaction, addrevisioncb=None):
2261 def addgroup(self, deltas, linkmapper, transaction, addrevisioncb=None):
2258 """
2262 """
2259 add a delta group
2263 add a delta group
2260
2264
2261 given a set of deltas, add them to the revision log. the
2265 given a set of deltas, add them to the revision log. the
2262 first delta is against its parent, which should be in our
2266 first delta is against its parent, which should be in our
2263 log, the rest are against the previous delta.
2267 log, the rest are against the previous delta.
2264
2268
2265 If ``addrevisioncb`` is defined, it will be called with arguments of
2269 If ``addrevisioncb`` is defined, it will be called with arguments of
2266 this revlog and the node that was added.
2270 this revlog and the node that was added.
2267 """
2271 """
2268
2272
2269 if self._writinghandles:
2273 if self._writinghandles:
2270 raise error.ProgrammingError(b'cannot nest addgroup() calls')
2274 raise error.ProgrammingError(b'cannot nest addgroup() calls')
2271
2275
2272 nodes = []
2276 nodes = []
2273
2277
2274 r = len(self)
2278 r = len(self)
2275 end = 0
2279 end = 0
2276 if r:
2280 if r:
2277 end = self.end(r - 1)
2281 end = self.end(r - 1)
2278 ifh = self._indexfp(b"a+")
2282 ifh = self._indexfp(b"a+")
2279 isize = r * self._io.size
2283 isize = r * self._io.size
2280 if self._inline:
2284 if self._inline:
2281 transaction.add(self.indexfile, end + isize, r)
2285 transaction.add(self.indexfile, end + isize, r)
2282 dfh = None
2286 dfh = None
2283 else:
2287 else:
2284 transaction.add(self.indexfile, isize, r)
2288 transaction.add(self.indexfile, isize, r)
2285 transaction.add(self.datafile, end)
2289 transaction.add(self.datafile, end)
2286 dfh = self._datafp(b"a+")
2290 dfh = self._datafp(b"a+")
2287
2291
2288 def flush():
2292 def flush():
2289 if dfh:
2293 if dfh:
2290 dfh.flush()
2294 dfh.flush()
2291 ifh.flush()
2295 ifh.flush()
2292
2296
2293 self._writinghandles = (ifh, dfh)
2297 self._writinghandles = (ifh, dfh)
2294
2298
2295 try:
2299 try:
2296 deltacomputer = deltautil.deltacomputer(self)
2300 deltacomputer = deltautil.deltacomputer(self)
2297 # loop through our set of deltas
2301 # loop through our set of deltas
2298 for data in deltas:
2302 for data in deltas:
2299 node, p1, p2, linknode, deltabase, delta, flags = data
2303 node, p1, p2, linknode, deltabase, delta, flags = data
2300 link = linkmapper(linknode)
2304 link = linkmapper(linknode)
2301 flags = flags or REVIDX_DEFAULT_FLAGS
2305 flags = flags or REVIDX_DEFAULT_FLAGS
2302
2306
2303 nodes.append(node)
2307 nodes.append(node)
2304
2308
2305 if node in self.nodemap:
2309 if node in self.nodemap:
2306 self._nodeduplicatecallback(transaction, node)
2310 self._nodeduplicatecallback(transaction, node)
2307 # this can happen if two branches make the same change
2311 # this can happen if two branches make the same change
2308 continue
2312 continue
2309
2313
2310 for p in (p1, p2):
2314 for p in (p1, p2):
2311 if p not in self.nodemap:
2315 if p not in self.nodemap:
2312 raise error.LookupError(
2316 raise error.LookupError(
2313 p, self.indexfile, _(b'unknown parent')
2317 p, self.indexfile, _(b'unknown parent')
2314 )
2318 )
2315
2319
2316 if deltabase not in self.nodemap:
2320 if deltabase not in self.nodemap:
2317 raise error.LookupError(
2321 raise error.LookupError(
2318 deltabase, self.indexfile, _(b'unknown delta base')
2322 deltabase, self.indexfile, _(b'unknown delta base')
2319 )
2323 )
2320
2324
2321 baserev = self.rev(deltabase)
2325 baserev = self.rev(deltabase)
2322
2326
2323 if baserev != nullrev and self.iscensored(baserev):
2327 if baserev != nullrev and self.iscensored(baserev):
2324 # if base is censored, delta must be full replacement in a
2328 # if base is censored, delta must be full replacement in a
2325 # single patch operation
2329 # single patch operation
2326 hlen = struct.calcsize(b">lll")
2330 hlen = struct.calcsize(b">lll")
2327 oldlen = self.rawsize(baserev)
2331 oldlen = self.rawsize(baserev)
2328 newlen = len(delta) - hlen
2332 newlen = len(delta) - hlen
2329 if delta[:hlen] != mdiff.replacediffheader(oldlen, newlen):
2333 if delta[:hlen] != mdiff.replacediffheader(oldlen, newlen):
2330 raise error.CensoredBaseError(
2334 raise error.CensoredBaseError(
2331 self.indexfile, self.node(baserev)
2335 self.indexfile, self.node(baserev)
2332 )
2336 )
2333
2337
2334 if not flags and self._peek_iscensored(baserev, delta, flush):
2338 if not flags and self._peek_iscensored(baserev, delta, flush):
2335 flags |= REVIDX_ISCENSORED
2339 flags |= REVIDX_ISCENSORED
2336
2340
2337 # We assume consumers of addrevisioncb will want to retrieve
2341 # We assume consumers of addrevisioncb will want to retrieve
2338 # the added revision, which will require a call to
2342 # the added revision, which will require a call to
2339 # revision(). revision() will fast path if there is a cache
2343 # revision(). revision() will fast path if there is a cache
2340 # hit. So, we tell _addrevision() to always cache in this case.
2344 # hit. So, we tell _addrevision() to always cache in this case.
2341 # We're only using addgroup() in the context of changegroup
2345 # We're only using addgroup() in the context of changegroup
2342 # generation so the revision data can always be handled as raw
2346 # generation so the revision data can always be handled as raw
2343 # by the flagprocessor.
2347 # by the flagprocessor.
2344 self._addrevision(
2348 self._addrevision(
2345 node,
2349 node,
2346 None,
2350 None,
2347 transaction,
2351 transaction,
2348 link,
2352 link,
2349 p1,
2353 p1,
2350 p2,
2354 p2,
2351 flags,
2355 flags,
2352 (baserev, delta),
2356 (baserev, delta),
2353 ifh,
2357 ifh,
2354 dfh,
2358 dfh,
2355 alwayscache=bool(addrevisioncb),
2359 alwayscache=bool(addrevisioncb),
2356 deltacomputer=deltacomputer,
2360 deltacomputer=deltacomputer,
2357 )
2361 )
2358
2362
2359 if addrevisioncb:
2363 if addrevisioncb:
2360 addrevisioncb(self, node)
2364 addrevisioncb(self, node)
2361
2365
2362 if not dfh and not self._inline:
2366 if not dfh and not self._inline:
2363 # addrevision switched from inline to conventional
2367 # addrevision switched from inline to conventional
2364 # reopen the index
2368 # reopen the index
2365 ifh.close()
2369 ifh.close()
2366 dfh = self._datafp(b"a+")
2370 dfh = self._datafp(b"a+")
2367 ifh = self._indexfp(b"a+")
2371 ifh = self._indexfp(b"a+")
2368 self._writinghandles = (ifh, dfh)
2372 self._writinghandles = (ifh, dfh)
2369 finally:
2373 finally:
2370 self._writinghandles = None
2374 self._writinghandles = None
2371
2375
2372 if dfh:
2376 if dfh:
2373 dfh.close()
2377 dfh.close()
2374 ifh.close()
2378 ifh.close()
2375
2379
2376 return nodes
2380 return nodes
2377
2381
2378 def iscensored(self, rev):
2382 def iscensored(self, rev):
2379 """Check if a file revision is censored."""
2383 """Check if a file revision is censored."""
2380 if not self._censorable:
2384 if not self._censorable:
2381 return False
2385 return False
2382
2386
2383 return self.flags(rev) & REVIDX_ISCENSORED
2387 return self.flags(rev) & REVIDX_ISCENSORED
2384
2388
2385 def _peek_iscensored(self, baserev, delta, flush):
2389 def _peek_iscensored(self, baserev, delta, flush):
2386 """Quickly check if a delta produces a censored revision."""
2390 """Quickly check if a delta produces a censored revision."""
2387 if not self._censorable:
2391 if not self._censorable:
2388 return False
2392 return False
2389
2393
2390 return storageutil.deltaiscensored(delta, baserev, self.rawsize)
2394 return storageutil.deltaiscensored(delta, baserev, self.rawsize)
2391
2395
2392 def getstrippoint(self, minlink):
2396 def getstrippoint(self, minlink):
2393 """find the minimum rev that must be stripped to strip the linkrev
2397 """find the minimum rev that must be stripped to strip the linkrev
2394
2398
2395 Returns a tuple containing the minimum rev and a set of all revs that
2399 Returns a tuple containing the minimum rev and a set of all revs that
2396 have linkrevs that will be broken by this strip.
2400 have linkrevs that will be broken by this strip.
2397 """
2401 """
2398 return storageutil.resolvestripinfo(
2402 return storageutil.resolvestripinfo(
2399 minlink,
2403 minlink,
2400 len(self) - 1,
2404 len(self) - 1,
2401 self.headrevs(),
2405 self.headrevs(),
2402 self.linkrev,
2406 self.linkrev,
2403 self.parentrevs,
2407 self.parentrevs,
2404 )
2408 )
2405
2409
2406 def strip(self, minlink, transaction):
2410 def strip(self, minlink, transaction):
2407 """truncate the revlog on the first revision with a linkrev >= minlink
2411 """truncate the revlog on the first revision with a linkrev >= minlink
2408
2412
2409 This function is called when we're stripping revision minlink and
2413 This function is called when we're stripping revision minlink and
2410 its descendants from the repository.
2414 its descendants from the repository.
2411
2415
2412 We have to remove all revisions with linkrev >= minlink, because
2416 We have to remove all revisions with linkrev >= minlink, because
2413 the equivalent changelog revisions will be renumbered after the
2417 the equivalent changelog revisions will be renumbered after the
2414 strip.
2418 strip.
2415
2419
2416 So we truncate the revlog on the first of these revisions, and
2420 So we truncate the revlog on the first of these revisions, and
2417 trust that the caller has saved the revisions that shouldn't be
2421 trust that the caller has saved the revisions that shouldn't be
2418 removed and that it'll re-add them after this truncation.
2422 removed and that it'll re-add them after this truncation.
2419 """
2423 """
2420 if len(self) == 0:
2424 if len(self) == 0:
2421 return
2425 return
2422
2426
2423 rev, _ = self.getstrippoint(minlink)
2427 rev, _ = self.getstrippoint(minlink)
2424 if rev == len(self):
2428 if rev == len(self):
2425 return
2429 return
2426
2430
2427 # first truncate the files on disk
2431 # first truncate the files on disk
2428 end = self.start(rev)
2432 end = self.start(rev)
2429 if not self._inline:
2433 if not self._inline:
2430 transaction.add(self.datafile, end)
2434 transaction.add(self.datafile, end)
2431 end = rev * self._io.size
2435 end = rev * self._io.size
2432 else:
2436 else:
2433 end += rev * self._io.size
2437 end += rev * self._io.size
2434
2438
2435 transaction.add(self.indexfile, end)
2439 transaction.add(self.indexfile, end)
2436
2440
2437 # then reset internal state in memory to forget those revisions
2441 # then reset internal state in memory to forget those revisions
2438 self._revisioncache = None
2442 self._revisioncache = None
2439 self._chaininfocache = {}
2443 self._chaininfocache = {}
2440 self._chunkclear()
2444 self._chunkclear()
2441
2445
2442 del self.index[rev:-1]
2446 del self.index[rev:-1]
2443 self._nodepos = None
2447 self._nodepos = None
2444
2448
2445 def checksize(self):
2449 def checksize(self):
2446 """Check size of index and data files
2450 """Check size of index and data files
2447
2451
2448 return a (dd, di) tuple.
2452 return a (dd, di) tuple.
2449 - dd: extra bytes for the "data" file
2453 - dd: extra bytes for the "data" file
2450 - di: extra bytes for the "index" file
2454 - di: extra bytes for the "index" file
2451
2455
2452 A healthy revlog will return (0, 0).
2456 A healthy revlog will return (0, 0).
2453 """
2457 """
2454 expected = 0
2458 expected = 0
2455 if len(self):
2459 if len(self):
2456 expected = max(0, self.end(len(self) - 1))
2460 expected = max(0, self.end(len(self) - 1))
2457
2461
2458 try:
2462 try:
2459 with self._datafp() as f:
2463 with self._datafp() as f:
2460 f.seek(0, io.SEEK_END)
2464 f.seek(0, io.SEEK_END)
2461 actual = f.tell()
2465 actual = f.tell()
2462 dd = actual - expected
2466 dd = actual - expected
2463 except IOError as inst:
2467 except IOError as inst:
2464 if inst.errno != errno.ENOENT:
2468 if inst.errno != errno.ENOENT:
2465 raise
2469 raise
2466 dd = 0
2470 dd = 0
2467
2471
2468 try:
2472 try:
2469 f = self.opener(self.indexfile)
2473 f = self.opener(self.indexfile)
2470 f.seek(0, io.SEEK_END)
2474 f.seek(0, io.SEEK_END)
2471 actual = f.tell()
2475 actual = f.tell()
2472 f.close()
2476 f.close()
2473 s = self._io.size
2477 s = self._io.size
2474 i = max(0, actual // s)
2478 i = max(0, actual // s)
2475 di = actual - (i * s)
2479 di = actual - (i * s)
2476 if self._inline:
2480 if self._inline:
2477 databytes = 0
2481 databytes = 0
2478 for r in self:
2482 for r in self:
2479 databytes += max(0, self.length(r))
2483 databytes += max(0, self.length(r))
2480 dd = 0
2484 dd = 0
2481 di = actual - len(self) * s - databytes
2485 di = actual - len(self) * s - databytes
2482 except IOError as inst:
2486 except IOError as inst:
2483 if inst.errno != errno.ENOENT:
2487 if inst.errno != errno.ENOENT:
2484 raise
2488 raise
2485 di = 0
2489 di = 0
2486
2490
2487 return (dd, di)
2491 return (dd, di)
2488
2492
2489 def files(self):
2493 def files(self):
2490 res = [self.indexfile]
2494 res = [self.indexfile]
2491 if not self._inline:
2495 if not self._inline:
2492 res.append(self.datafile)
2496 res.append(self.datafile)
2493 return res
2497 return res
2494
2498
2495 def emitrevisions(
2499 def emitrevisions(
2496 self,
2500 self,
2497 nodes,
2501 nodes,
2498 nodesorder=None,
2502 nodesorder=None,
2499 revisiondata=False,
2503 revisiondata=False,
2500 assumehaveparentrevisions=False,
2504 assumehaveparentrevisions=False,
2501 deltamode=repository.CG_DELTAMODE_STD,
2505 deltamode=repository.CG_DELTAMODE_STD,
2502 ):
2506 ):
2503 if nodesorder not in (b'nodes', b'storage', b'linear', None):
2507 if nodesorder not in (b'nodes', b'storage', b'linear', None):
2504 raise error.ProgrammingError(
2508 raise error.ProgrammingError(
2505 b'unhandled value for nodesorder: %s' % nodesorder
2509 b'unhandled value for nodesorder: %s' % nodesorder
2506 )
2510 )
2507
2511
2508 if nodesorder is None and not self._generaldelta:
2512 if nodesorder is None and not self._generaldelta:
2509 nodesorder = b'storage'
2513 nodesorder = b'storage'
2510
2514
2511 if (
2515 if (
2512 not self._storedeltachains
2516 not self._storedeltachains
2513 and deltamode != repository.CG_DELTAMODE_PREV
2517 and deltamode != repository.CG_DELTAMODE_PREV
2514 ):
2518 ):
2515 deltamode = repository.CG_DELTAMODE_FULL
2519 deltamode = repository.CG_DELTAMODE_FULL
2516
2520
2517 return storageutil.emitrevisions(
2521 return storageutil.emitrevisions(
2518 self,
2522 self,
2519 nodes,
2523 nodes,
2520 nodesorder,
2524 nodesorder,
2521 revlogrevisiondelta,
2525 revlogrevisiondelta,
2522 deltaparentfn=self.deltaparent,
2526 deltaparentfn=self.deltaparent,
2523 candeltafn=self.candelta,
2527 candeltafn=self.candelta,
2524 rawsizefn=self.rawsize,
2528 rawsizefn=self.rawsize,
2525 revdifffn=self.revdiff,
2529 revdifffn=self.revdiff,
2526 flagsfn=self.flags,
2530 flagsfn=self.flags,
2527 deltamode=deltamode,
2531 deltamode=deltamode,
2528 revisiondata=revisiondata,
2532 revisiondata=revisiondata,
2529 assumehaveparentrevisions=assumehaveparentrevisions,
2533 assumehaveparentrevisions=assumehaveparentrevisions,
2530 )
2534 )
2531
2535
2532 DELTAREUSEALWAYS = b'always'
2536 DELTAREUSEALWAYS = b'always'
2533 DELTAREUSESAMEREVS = b'samerevs'
2537 DELTAREUSESAMEREVS = b'samerevs'
2534 DELTAREUSENEVER = b'never'
2538 DELTAREUSENEVER = b'never'
2535
2539
2536 DELTAREUSEFULLADD = b'fulladd'
2540 DELTAREUSEFULLADD = b'fulladd'
2537
2541
2538 DELTAREUSEALL = {b'always', b'samerevs', b'never', b'fulladd'}
2542 DELTAREUSEALL = {b'always', b'samerevs', b'never', b'fulladd'}
2539
2543
2540 def clone(
2544 def clone(
2541 self,
2545 self,
2542 tr,
2546 tr,
2543 destrevlog,
2547 destrevlog,
2544 addrevisioncb=None,
2548 addrevisioncb=None,
2545 deltareuse=DELTAREUSESAMEREVS,
2549 deltareuse=DELTAREUSESAMEREVS,
2546 forcedeltabothparents=None,
2550 forcedeltabothparents=None,
2547 sidedatacompanion=None,
2551 sidedatacompanion=None,
2548 ):
2552 ):
2549 """Copy this revlog to another, possibly with format changes.
2553 """Copy this revlog to another, possibly with format changes.
2550
2554
2551 The destination revlog will contain the same revisions and nodes.
2555 The destination revlog will contain the same revisions and nodes.
2552 However, it may not be bit-for-bit identical due to e.g. delta encoding
2556 However, it may not be bit-for-bit identical due to e.g. delta encoding
2553 differences.
2557 differences.
2554
2558
2555 The ``deltareuse`` argument control how deltas from the existing revlog
2559 The ``deltareuse`` argument control how deltas from the existing revlog
2556 are preserved in the destination revlog. The argument can have the
2560 are preserved in the destination revlog. The argument can have the
2557 following values:
2561 following values:
2558
2562
2559 DELTAREUSEALWAYS
2563 DELTAREUSEALWAYS
2560 Deltas will always be reused (if possible), even if the destination
2564 Deltas will always be reused (if possible), even if the destination
2561 revlog would not select the same revisions for the delta. This is the
2565 revlog would not select the same revisions for the delta. This is the
2562 fastest mode of operation.
2566 fastest mode of operation.
2563 DELTAREUSESAMEREVS
2567 DELTAREUSESAMEREVS
2564 Deltas will be reused if the destination revlog would pick the same
2568 Deltas will be reused if the destination revlog would pick the same
2565 revisions for the delta. This mode strikes a balance between speed
2569 revisions for the delta. This mode strikes a balance between speed
2566 and optimization.
2570 and optimization.
2567 DELTAREUSENEVER
2571 DELTAREUSENEVER
2568 Deltas will never be reused. This is the slowest mode of execution.
2572 Deltas will never be reused. This is the slowest mode of execution.
2569 This mode can be used to recompute deltas (e.g. if the diff/delta
2573 This mode can be used to recompute deltas (e.g. if the diff/delta
2570 algorithm changes).
2574 algorithm changes).
2571 DELTAREUSEFULLADD
2575 DELTAREUSEFULLADD
2572 Revision will be re-added as if their were new content. This is
2576 Revision will be re-added as if their were new content. This is
2573 slower than DELTAREUSEALWAYS but allow more mechanism to kicks in.
2577 slower than DELTAREUSEALWAYS but allow more mechanism to kicks in.
2574 eg: large file detection and handling.
2578 eg: large file detection and handling.
2575
2579
2576 Delta computation can be slow, so the choice of delta reuse policy can
2580 Delta computation can be slow, so the choice of delta reuse policy can
2577 significantly affect run time.
2581 significantly affect run time.
2578
2582
2579 The default policy (``DELTAREUSESAMEREVS``) strikes a balance between
2583 The default policy (``DELTAREUSESAMEREVS``) strikes a balance between
2580 two extremes. Deltas will be reused if they are appropriate. But if the
2584 two extremes. Deltas will be reused if they are appropriate. But if the
2581 delta could choose a better revision, it will do so. This means if you
2585 delta could choose a better revision, it will do so. This means if you
2582 are converting a non-generaldelta revlog to a generaldelta revlog,
2586 are converting a non-generaldelta revlog to a generaldelta revlog,
2583 deltas will be recomputed if the delta's parent isn't a parent of the
2587 deltas will be recomputed if the delta's parent isn't a parent of the
2584 revision.
2588 revision.
2585
2589
2586 In addition to the delta policy, the ``forcedeltabothparents``
2590 In addition to the delta policy, the ``forcedeltabothparents``
2587 argument controls whether to force compute deltas against both parents
2591 argument controls whether to force compute deltas against both parents
2588 for merges. By default, the current default is used.
2592 for merges. By default, the current default is used.
2589
2593
2590 If not None, the `sidedatacompanion` is callable that accept two
2594 If not None, the `sidedatacompanion` is callable that accept two
2591 arguments:
2595 arguments:
2592
2596
2593 (srcrevlog, rev)
2597 (srcrevlog, rev)
2594
2598
2595 and return a triplet that control changes to sidedata content from the
2599 and return a triplet that control changes to sidedata content from the
2596 old revision to the new clone result:
2600 old revision to the new clone result:
2597
2601
2598 (dropall, filterout, update)
2602 (dropall, filterout, update)
2599
2603
2600 * if `dropall` is True, all sidedata should be dropped
2604 * if `dropall` is True, all sidedata should be dropped
2601 * `filterout` is a set of sidedata keys that should be dropped
2605 * `filterout` is a set of sidedata keys that should be dropped
2602 * `update` is a mapping of additionnal/new key -> value
2606 * `update` is a mapping of additionnal/new key -> value
2603 """
2607 """
2604 if deltareuse not in self.DELTAREUSEALL:
2608 if deltareuse not in self.DELTAREUSEALL:
2605 raise ValueError(
2609 raise ValueError(
2606 _(b'value for deltareuse invalid: %s') % deltareuse
2610 _(b'value for deltareuse invalid: %s') % deltareuse
2607 )
2611 )
2608
2612
2609 if len(destrevlog):
2613 if len(destrevlog):
2610 raise ValueError(_(b'destination revlog is not empty'))
2614 raise ValueError(_(b'destination revlog is not empty'))
2611
2615
2612 if getattr(self, 'filteredrevs', None):
2616 if getattr(self, 'filteredrevs', None):
2613 raise ValueError(_(b'source revlog has filtered revisions'))
2617 raise ValueError(_(b'source revlog has filtered revisions'))
2614 if getattr(destrevlog, 'filteredrevs', None):
2618 if getattr(destrevlog, 'filteredrevs', None):
2615 raise ValueError(_(b'destination revlog has filtered revisions'))
2619 raise ValueError(_(b'destination revlog has filtered revisions'))
2616
2620
2617 # lazydelta and lazydeltabase controls whether to reuse a cached delta,
2621 # lazydelta and lazydeltabase controls whether to reuse a cached delta,
2618 # if possible.
2622 # if possible.
2619 oldlazydelta = destrevlog._lazydelta
2623 oldlazydelta = destrevlog._lazydelta
2620 oldlazydeltabase = destrevlog._lazydeltabase
2624 oldlazydeltabase = destrevlog._lazydeltabase
2621 oldamd = destrevlog._deltabothparents
2625 oldamd = destrevlog._deltabothparents
2622
2626
2623 try:
2627 try:
2624 if deltareuse == self.DELTAREUSEALWAYS:
2628 if deltareuse == self.DELTAREUSEALWAYS:
2625 destrevlog._lazydeltabase = True
2629 destrevlog._lazydeltabase = True
2626 destrevlog._lazydelta = True
2630 destrevlog._lazydelta = True
2627 elif deltareuse == self.DELTAREUSESAMEREVS:
2631 elif deltareuse == self.DELTAREUSESAMEREVS:
2628 destrevlog._lazydeltabase = False
2632 destrevlog._lazydeltabase = False
2629 destrevlog._lazydelta = True
2633 destrevlog._lazydelta = True
2630 elif deltareuse == self.DELTAREUSENEVER:
2634 elif deltareuse == self.DELTAREUSENEVER:
2631 destrevlog._lazydeltabase = False
2635 destrevlog._lazydeltabase = False
2632 destrevlog._lazydelta = False
2636 destrevlog._lazydelta = False
2633
2637
2634 destrevlog._deltabothparents = forcedeltabothparents or oldamd
2638 destrevlog._deltabothparents = forcedeltabothparents or oldamd
2635
2639
2636 self._clone(
2640 self._clone(
2637 tr,
2641 tr,
2638 destrevlog,
2642 destrevlog,
2639 addrevisioncb,
2643 addrevisioncb,
2640 deltareuse,
2644 deltareuse,
2641 forcedeltabothparents,
2645 forcedeltabothparents,
2642 sidedatacompanion,
2646 sidedatacompanion,
2643 )
2647 )
2644
2648
2645 finally:
2649 finally:
2646 destrevlog._lazydelta = oldlazydelta
2650 destrevlog._lazydelta = oldlazydelta
2647 destrevlog._lazydeltabase = oldlazydeltabase
2651 destrevlog._lazydeltabase = oldlazydeltabase
2648 destrevlog._deltabothparents = oldamd
2652 destrevlog._deltabothparents = oldamd
2649
2653
2650 def _clone(
2654 def _clone(
2651 self,
2655 self,
2652 tr,
2656 tr,
2653 destrevlog,
2657 destrevlog,
2654 addrevisioncb,
2658 addrevisioncb,
2655 deltareuse,
2659 deltareuse,
2656 forcedeltabothparents,
2660 forcedeltabothparents,
2657 sidedatacompanion,
2661 sidedatacompanion,
2658 ):
2662 ):
2659 """perform the core duty of `revlog.clone` after parameter processing"""
2663 """perform the core duty of `revlog.clone` after parameter processing"""
2660 deltacomputer = deltautil.deltacomputer(destrevlog)
2664 deltacomputer = deltautil.deltacomputer(destrevlog)
2661 index = self.index
2665 index = self.index
2662 for rev in self:
2666 for rev in self:
2663 entry = index[rev]
2667 entry = index[rev]
2664
2668
2665 # Some classes override linkrev to take filtered revs into
2669 # Some classes override linkrev to take filtered revs into
2666 # account. Use raw entry from index.
2670 # account. Use raw entry from index.
2667 flags = entry[0] & 0xFFFF
2671 flags = entry[0] & 0xFFFF
2668 linkrev = entry[4]
2672 linkrev = entry[4]
2669 p1 = index[entry[5]][7]
2673 p1 = index[entry[5]][7]
2670 p2 = index[entry[6]][7]
2674 p2 = index[entry[6]][7]
2671 node = entry[7]
2675 node = entry[7]
2672
2676
2673 sidedataactions = (False, [], {})
2677 sidedataactions = (False, [], {})
2674 if sidedatacompanion is not None:
2678 if sidedatacompanion is not None:
2675 sidedataactions = sidedatacompanion(self, rev)
2679 sidedataactions = sidedatacompanion(self, rev)
2676
2680
2677 # (Possibly) reuse the delta from the revlog if allowed and
2681 # (Possibly) reuse the delta from the revlog if allowed and
2678 # the revlog chunk is a delta.
2682 # the revlog chunk is a delta.
2679 cachedelta = None
2683 cachedelta = None
2680 rawtext = None
2684 rawtext = None
2681 if any(sidedataactions) or deltareuse == self.DELTAREUSEFULLADD:
2685 if any(sidedataactions) or deltareuse == self.DELTAREUSEFULLADD:
2682 dropall, filterout, update = sidedataactions
2686 dropall, filterout, update = sidedataactions
2683 text, sidedata = self._revisiondata(rev)
2687 text, sidedata = self._revisiondata(rev)
2684 if dropall:
2688 if dropall:
2685 sidedata = {}
2689 sidedata = {}
2686 for key in filterout:
2690 for key in filterout:
2687 sidedata.pop(key, None)
2691 sidedata.pop(key, None)
2688 sidedata.update(update)
2692 sidedata.update(update)
2689 if not sidedata:
2693 if not sidedata:
2690 sidedata = None
2694 sidedata = None
2691 destrevlog.addrevision(
2695 destrevlog.addrevision(
2692 text,
2696 text,
2693 tr,
2697 tr,
2694 linkrev,
2698 linkrev,
2695 p1,
2699 p1,
2696 p2,
2700 p2,
2697 cachedelta=cachedelta,
2701 cachedelta=cachedelta,
2698 node=node,
2702 node=node,
2699 flags=flags,
2703 flags=flags,
2700 deltacomputer=deltacomputer,
2704 deltacomputer=deltacomputer,
2701 sidedata=sidedata,
2705 sidedata=sidedata,
2702 )
2706 )
2703 else:
2707 else:
2704 if destrevlog._lazydelta:
2708 if destrevlog._lazydelta:
2705 dp = self.deltaparent(rev)
2709 dp = self.deltaparent(rev)
2706 if dp != nullrev:
2710 if dp != nullrev:
2707 cachedelta = (dp, bytes(self._chunk(rev)))
2711 cachedelta = (dp, bytes(self._chunk(rev)))
2708
2712
2709 if not cachedelta:
2713 if not cachedelta:
2710 rawtext = self.rawdata(rev)
2714 rawtext = self.rawdata(rev)
2711
2715
2712 ifh = destrevlog.opener(
2716 ifh = destrevlog.opener(
2713 destrevlog.indexfile, b'a+', checkambig=False
2717 destrevlog.indexfile, b'a+', checkambig=False
2714 )
2718 )
2715 dfh = None
2719 dfh = None
2716 if not destrevlog._inline:
2720 if not destrevlog._inline:
2717 dfh = destrevlog.opener(destrevlog.datafile, b'a+')
2721 dfh = destrevlog.opener(destrevlog.datafile, b'a+')
2718 try:
2722 try:
2719 destrevlog._addrevision(
2723 destrevlog._addrevision(
2720 node,
2724 node,
2721 rawtext,
2725 rawtext,
2722 tr,
2726 tr,
2723 linkrev,
2727 linkrev,
2724 p1,
2728 p1,
2725 p2,
2729 p2,
2726 flags,
2730 flags,
2727 cachedelta,
2731 cachedelta,
2728 ifh,
2732 ifh,
2729 dfh,
2733 dfh,
2730 deltacomputer=deltacomputer,
2734 deltacomputer=deltacomputer,
2731 )
2735 )
2732 finally:
2736 finally:
2733 if dfh:
2737 if dfh:
2734 dfh.close()
2738 dfh.close()
2735 ifh.close()
2739 ifh.close()
2736
2740
2737 if addrevisioncb:
2741 if addrevisioncb:
2738 addrevisioncb(self, rev, node)
2742 addrevisioncb(self, rev, node)
2739
2743
2740 def censorrevision(self, tr, censornode, tombstone=b''):
2744 def censorrevision(self, tr, censornode, tombstone=b''):
2741 if (self.version & 0xFFFF) == REVLOGV0:
2745 if (self.version & 0xFFFF) == REVLOGV0:
2742 raise error.RevlogError(
2746 raise error.RevlogError(
2743 _(b'cannot censor with version %d revlogs') % self.version
2747 _(b'cannot censor with version %d revlogs') % self.version
2744 )
2748 )
2745
2749
2746 censorrev = self.rev(censornode)
2750 censorrev = self.rev(censornode)
2747 tombstone = storageutil.packmeta({b'censored': tombstone}, b'')
2751 tombstone = storageutil.packmeta({b'censored': tombstone}, b'')
2748
2752
2749 if len(tombstone) > self.rawsize(censorrev):
2753 if len(tombstone) > self.rawsize(censorrev):
2750 raise error.Abort(
2754 raise error.Abort(
2751 _(b'censor tombstone must be no longer than censored data')
2755 _(b'censor tombstone must be no longer than censored data')
2752 )
2756 )
2753
2757
2754 # Rewriting the revlog in place is hard. Our strategy for censoring is
2758 # Rewriting the revlog in place is hard. Our strategy for censoring is
2755 # to create a new revlog, copy all revisions to it, then replace the
2759 # to create a new revlog, copy all revisions to it, then replace the
2756 # revlogs on transaction close.
2760 # revlogs on transaction close.
2757
2761
2758 newindexfile = self.indexfile + b'.tmpcensored'
2762 newindexfile = self.indexfile + b'.tmpcensored'
2759 newdatafile = self.datafile + b'.tmpcensored'
2763 newdatafile = self.datafile + b'.tmpcensored'
2760
2764
2761 # This is a bit dangerous. We could easily have a mismatch of state.
2765 # This is a bit dangerous. We could easily have a mismatch of state.
2762 newrl = revlog(self.opener, newindexfile, newdatafile, censorable=True)
2766 newrl = revlog(self.opener, newindexfile, newdatafile, censorable=True)
2763 newrl.version = self.version
2767 newrl.version = self.version
2764 newrl._generaldelta = self._generaldelta
2768 newrl._generaldelta = self._generaldelta
2765 newrl._io = self._io
2769 newrl._io = self._io
2766
2770
2767 for rev in self.revs():
2771 for rev in self.revs():
2768 node = self.node(rev)
2772 node = self.node(rev)
2769 p1, p2 = self.parents(node)
2773 p1, p2 = self.parents(node)
2770
2774
2771 if rev == censorrev:
2775 if rev == censorrev:
2772 newrl.addrawrevision(
2776 newrl.addrawrevision(
2773 tombstone,
2777 tombstone,
2774 tr,
2778 tr,
2775 self.linkrev(censorrev),
2779 self.linkrev(censorrev),
2776 p1,
2780 p1,
2777 p2,
2781 p2,
2778 censornode,
2782 censornode,
2779 REVIDX_ISCENSORED,
2783 REVIDX_ISCENSORED,
2780 )
2784 )
2781
2785
2782 if newrl.deltaparent(rev) != nullrev:
2786 if newrl.deltaparent(rev) != nullrev:
2783 raise error.Abort(
2787 raise error.Abort(
2784 _(
2788 _(
2785 b'censored revision stored as delta; '
2789 b'censored revision stored as delta; '
2786 b'cannot censor'
2790 b'cannot censor'
2787 ),
2791 ),
2788 hint=_(
2792 hint=_(
2789 b'censoring of revlogs is not '
2793 b'censoring of revlogs is not '
2790 b'fully implemented; please report '
2794 b'fully implemented; please report '
2791 b'this bug'
2795 b'this bug'
2792 ),
2796 ),
2793 )
2797 )
2794 continue
2798 continue
2795
2799
2796 if self.iscensored(rev):
2800 if self.iscensored(rev):
2797 if self.deltaparent(rev) != nullrev:
2801 if self.deltaparent(rev) != nullrev:
2798 raise error.Abort(
2802 raise error.Abort(
2799 _(
2803 _(
2800 b'cannot censor due to censored '
2804 b'cannot censor due to censored '
2801 b'revision having delta stored'
2805 b'revision having delta stored'
2802 )
2806 )
2803 )
2807 )
2804 rawtext = self._chunk(rev)
2808 rawtext = self._chunk(rev)
2805 else:
2809 else:
2806 rawtext = self.rawdata(rev)
2810 rawtext = self.rawdata(rev)
2807
2811
2808 newrl.addrawrevision(
2812 newrl.addrawrevision(
2809 rawtext, tr, self.linkrev(rev), p1, p2, node, self.flags(rev)
2813 rawtext, tr, self.linkrev(rev), p1, p2, node, self.flags(rev)
2810 )
2814 )
2811
2815
2812 tr.addbackup(self.indexfile, location=b'store')
2816 tr.addbackup(self.indexfile, location=b'store')
2813 if not self._inline:
2817 if not self._inline:
2814 tr.addbackup(self.datafile, location=b'store')
2818 tr.addbackup(self.datafile, location=b'store')
2815
2819
2816 self.opener.rename(newrl.indexfile, self.indexfile)
2820 self.opener.rename(newrl.indexfile, self.indexfile)
2817 if not self._inline:
2821 if not self._inline:
2818 self.opener.rename(newrl.datafile, self.datafile)
2822 self.opener.rename(newrl.datafile, self.datafile)
2819
2823
2820 self.clearcaches()
2824 self.clearcaches()
2821 self._loadindex()
2825 self._loadindex()
2822
2826
2823 def verifyintegrity(self, state):
2827 def verifyintegrity(self, state):
2824 """Verifies the integrity of the revlog.
2828 """Verifies the integrity of the revlog.
2825
2829
2826 Yields ``revlogproblem`` instances describing problems that are
2830 Yields ``revlogproblem`` instances describing problems that are
2827 found.
2831 found.
2828 """
2832 """
2829 dd, di = self.checksize()
2833 dd, di = self.checksize()
2830 if dd:
2834 if dd:
2831 yield revlogproblem(error=_(b'data length off by %d bytes') % dd)
2835 yield revlogproblem(error=_(b'data length off by %d bytes') % dd)
2832 if di:
2836 if di:
2833 yield revlogproblem(error=_(b'index contains %d extra bytes') % di)
2837 yield revlogproblem(error=_(b'index contains %d extra bytes') % di)
2834
2838
2835 version = self.version & 0xFFFF
2839 version = self.version & 0xFFFF
2836
2840
2837 # The verifier tells us what version revlog we should be.
2841 # The verifier tells us what version revlog we should be.
2838 if version != state[b'expectedversion']:
2842 if version != state[b'expectedversion']:
2839 yield revlogproblem(
2843 yield revlogproblem(
2840 warning=_(b"warning: '%s' uses revlog format %d; expected %d")
2844 warning=_(b"warning: '%s' uses revlog format %d; expected %d")
2841 % (self.indexfile, version, state[b'expectedversion'])
2845 % (self.indexfile, version, state[b'expectedversion'])
2842 )
2846 )
2843
2847
2844 state[b'skipread'] = set()
2848 state[b'skipread'] = set()
2845
2849
2846 for rev in self:
2850 for rev in self:
2847 node = self.node(rev)
2851 node = self.node(rev)
2848
2852
2849 # Verify contents. 4 cases to care about:
2853 # Verify contents. 4 cases to care about:
2850 #
2854 #
2851 # common: the most common case
2855 # common: the most common case
2852 # rename: with a rename
2856 # rename: with a rename
2853 # meta: file content starts with b'\1\n', the metadata
2857 # meta: file content starts with b'\1\n', the metadata
2854 # header defined in filelog.py, but without a rename
2858 # header defined in filelog.py, but without a rename
2855 # ext: content stored externally
2859 # ext: content stored externally
2856 #
2860 #
2857 # More formally, their differences are shown below:
2861 # More formally, their differences are shown below:
2858 #
2862 #
2859 # | common | rename | meta | ext
2863 # | common | rename | meta | ext
2860 # -------------------------------------------------------
2864 # -------------------------------------------------------
2861 # flags() | 0 | 0 | 0 | not 0
2865 # flags() | 0 | 0 | 0 | not 0
2862 # renamed() | False | True | False | ?
2866 # renamed() | False | True | False | ?
2863 # rawtext[0:2]=='\1\n'| False | True | True | ?
2867 # rawtext[0:2]=='\1\n'| False | True | True | ?
2864 #
2868 #
2865 # "rawtext" means the raw text stored in revlog data, which
2869 # "rawtext" means the raw text stored in revlog data, which
2866 # could be retrieved by "rawdata(rev)". "text"
2870 # could be retrieved by "rawdata(rev)". "text"
2867 # mentioned below is "revision(rev)".
2871 # mentioned below is "revision(rev)".
2868 #
2872 #
2869 # There are 3 different lengths stored physically:
2873 # There are 3 different lengths stored physically:
2870 # 1. L1: rawsize, stored in revlog index
2874 # 1. L1: rawsize, stored in revlog index
2871 # 2. L2: len(rawtext), stored in revlog data
2875 # 2. L2: len(rawtext), stored in revlog data
2872 # 3. L3: len(text), stored in revlog data if flags==0, or
2876 # 3. L3: len(text), stored in revlog data if flags==0, or
2873 # possibly somewhere else if flags!=0
2877 # possibly somewhere else if flags!=0
2874 #
2878 #
2875 # L1 should be equal to L2. L3 could be different from them.
2879 # L1 should be equal to L2. L3 could be different from them.
2876 # "text" may or may not affect commit hash depending on flag
2880 # "text" may or may not affect commit hash depending on flag
2877 # processors (see flagutil.addflagprocessor).
2881 # processors (see flagutil.addflagprocessor).
2878 #
2882 #
2879 # | common | rename | meta | ext
2883 # | common | rename | meta | ext
2880 # -------------------------------------------------
2884 # -------------------------------------------------
2881 # rawsize() | L1 | L1 | L1 | L1
2885 # rawsize() | L1 | L1 | L1 | L1
2882 # size() | L1 | L2-LM | L1(*) | L1 (?)
2886 # size() | L1 | L2-LM | L1(*) | L1 (?)
2883 # len(rawtext) | L2 | L2 | L2 | L2
2887 # len(rawtext) | L2 | L2 | L2 | L2
2884 # len(text) | L2 | L2 | L2 | L3
2888 # len(text) | L2 | L2 | L2 | L3
2885 # len(read()) | L2 | L2-LM | L2-LM | L3 (?)
2889 # len(read()) | L2 | L2-LM | L2-LM | L3 (?)
2886 #
2890 #
2887 # LM: length of metadata, depending on rawtext
2891 # LM: length of metadata, depending on rawtext
2888 # (*): not ideal, see comment in filelog.size
2892 # (*): not ideal, see comment in filelog.size
2889 # (?): could be "- len(meta)" if the resolved content has
2893 # (?): could be "- len(meta)" if the resolved content has
2890 # rename metadata
2894 # rename metadata
2891 #
2895 #
2892 # Checks needed to be done:
2896 # Checks needed to be done:
2893 # 1. length check: L1 == L2, in all cases.
2897 # 1. length check: L1 == L2, in all cases.
2894 # 2. hash check: depending on flag processor, we may need to
2898 # 2. hash check: depending on flag processor, we may need to
2895 # use either "text" (external), or "rawtext" (in revlog).
2899 # use either "text" (external), or "rawtext" (in revlog).
2896
2900
2897 try:
2901 try:
2898 skipflags = state.get(b'skipflags', 0)
2902 skipflags = state.get(b'skipflags', 0)
2899 if skipflags:
2903 if skipflags:
2900 skipflags &= self.flags(rev)
2904 skipflags &= self.flags(rev)
2901
2905
2902 if skipflags:
2906 if skipflags:
2903 state[b'skipread'].add(node)
2907 state[b'skipread'].add(node)
2904 else:
2908 else:
2905 # Side-effect: read content and verify hash.
2909 # Side-effect: read content and verify hash.
2906 self.revision(node)
2910 self.revision(node)
2907
2911
2908 l1 = self.rawsize(rev)
2912 l1 = self.rawsize(rev)
2909 l2 = len(self.rawdata(node))
2913 l2 = len(self.rawdata(node))
2910
2914
2911 if l1 != l2:
2915 if l1 != l2:
2912 yield revlogproblem(
2916 yield revlogproblem(
2913 error=_(b'unpacked size is %d, %d expected') % (l2, l1),
2917 error=_(b'unpacked size is %d, %d expected') % (l2, l1),
2914 node=node,
2918 node=node,
2915 )
2919 )
2916
2920
2917 except error.CensoredNodeError:
2921 except error.CensoredNodeError:
2918 if state[b'erroroncensored']:
2922 if state[b'erroroncensored']:
2919 yield revlogproblem(
2923 yield revlogproblem(
2920 error=_(b'censored file data'), node=node
2924 error=_(b'censored file data'), node=node
2921 )
2925 )
2922 state[b'skipread'].add(node)
2926 state[b'skipread'].add(node)
2923 except Exception as e:
2927 except Exception as e:
2924 yield revlogproblem(
2928 yield revlogproblem(
2925 error=_(b'unpacking %s: %s')
2929 error=_(b'unpacking %s: %s')
2926 % (short(node), stringutil.forcebytestr(e)),
2930 % (short(node), stringutil.forcebytestr(e)),
2927 node=node,
2931 node=node,
2928 )
2932 )
2929 state[b'skipread'].add(node)
2933 state[b'skipread'].add(node)
2930
2934
2931 def storageinfo(
2935 def storageinfo(
2932 self,
2936 self,
2933 exclusivefiles=False,
2937 exclusivefiles=False,
2934 sharedfiles=False,
2938 sharedfiles=False,
2935 revisionscount=False,
2939 revisionscount=False,
2936 trackedsize=False,
2940 trackedsize=False,
2937 storedsize=False,
2941 storedsize=False,
2938 ):
2942 ):
2939 d = {}
2943 d = {}
2940
2944
2941 if exclusivefiles:
2945 if exclusivefiles:
2942 d[b'exclusivefiles'] = [(self.opener, self.indexfile)]
2946 d[b'exclusivefiles'] = [(self.opener, self.indexfile)]
2943 if not self._inline:
2947 if not self._inline:
2944 d[b'exclusivefiles'].append((self.opener, self.datafile))
2948 d[b'exclusivefiles'].append((self.opener, self.datafile))
2945
2949
2946 if sharedfiles:
2950 if sharedfiles:
2947 d[b'sharedfiles'] = []
2951 d[b'sharedfiles'] = []
2948
2952
2949 if revisionscount:
2953 if revisionscount:
2950 d[b'revisionscount'] = len(self)
2954 d[b'revisionscount'] = len(self)
2951
2955
2952 if trackedsize:
2956 if trackedsize:
2953 d[b'trackedsize'] = sum(map(self.rawsize, iter(self)))
2957 d[b'trackedsize'] = sum(map(self.rawsize, iter(self)))
2954
2958
2955 if storedsize:
2959 if storedsize:
2956 d[b'storedsize'] = sum(
2960 d[b'storedsize'] = sum(
2957 self.opener.stat(path).st_size for path in self.files()
2961 self.opener.stat(path).st_size for path in self.files()
2958 )
2962 )
2959
2963
2960 return d
2964 return d
General Comments 0
You need to be logged in to leave comments. Login now