##// END OF EJS Templates
dirstate-entry: introduce dedicated accessors for v1 serialization...
marmoute -
r48298:80dc1d45 default
parent child Browse files
Show More
@@ -1,763 +1,795 b''
1 /*
1 /*
2 parsers.c - efficient content parsing
2 parsers.c - efficient content parsing
3
3
4 Copyright 2008 Olivia Mackall <olivia@selenic.com> and others
4 Copyright 2008 Olivia Mackall <olivia@selenic.com> and others
5
5
6 This software may be used and distributed according to the terms of
6 This software may be used and distributed according to the terms of
7 the GNU General Public License, incorporated herein by reference.
7 the GNU General Public License, incorporated herein by reference.
8 */
8 */
9
9
10 #define PY_SSIZE_T_CLEAN
10 #define PY_SSIZE_T_CLEAN
11 #include <Python.h>
11 #include <Python.h>
12 #include <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 static PyObject *dirstatetuple_v1_state(dirstateTupleObject *self)
122 {
123 return PyBytes_FromStringAndSize(&self->state, 1);
124 };
125
126 static PyObject *dirstatetuple_v1_mode(dirstateTupleObject *self)
127 {
128 return PyInt_FromLong(self->mode);
129 };
130
131 static PyObject *dirstatetuple_v1_size(dirstateTupleObject *self)
132 {
133 return PyInt_FromLong(self->size);
134 };
135
136 static PyObject *dirstatetuple_v1_mtime(dirstateTupleObject *self)
137 {
138 return PyInt_FromLong(self->mtime);
139 };
140
141 static PyMethodDef dirstatetuple_methods[] = {
142 {"v1_state", (PyCFunction)dirstatetuple_v1_state, METH_NOARGS,
143 "return a \"state\" suitable for v1 serialization"},
144 {"v1_mode", (PyCFunction)dirstatetuple_v1_mode, METH_NOARGS,
145 "return a \"mode\" suitable for v1 serialization"},
146 {"v1_size", (PyCFunction)dirstatetuple_v1_size, METH_NOARGS,
147 "return a \"size\" suitable for v1 serialization"},
148 {"v1_mtime", (PyCFunction)dirstatetuple_v1_mtime, METH_NOARGS,
149 "return a \"mtime\" suitable for v1 serialization"},
150 {NULL} /* Sentinel */
151 };
152
121 PyTypeObject dirstateTupleType = {
153 PyTypeObject dirstateTupleType = {
122 PyVarObject_HEAD_INIT(NULL, 0) /* header */
154 PyVarObject_HEAD_INIT(NULL, 0) /* header */
123 "dirstate_tuple", /* tp_name */
155 "dirstate_tuple", /* tp_name */
124 sizeof(dirstateTupleObject), /* tp_basicsize */
156 sizeof(dirstateTupleObject), /* tp_basicsize */
125 0, /* tp_itemsize */
157 0, /* tp_itemsize */
126 (destructor)dirstate_tuple_dealloc, /* tp_dealloc */
158 (destructor)dirstate_tuple_dealloc, /* tp_dealloc */
127 0, /* tp_print */
159 0, /* tp_print */
128 0, /* tp_getattr */
160 0, /* tp_getattr */
129 0, /* tp_setattr */
161 0, /* tp_setattr */
130 0, /* tp_compare */
162 0, /* tp_compare */
131 0, /* tp_repr */
163 0, /* tp_repr */
132 0, /* tp_as_number */
164 0, /* tp_as_number */
133 &dirstate_tuple_sq, /* tp_as_sequence */
165 &dirstate_tuple_sq, /* tp_as_sequence */
134 0, /* tp_as_mapping */
166 0, /* tp_as_mapping */
135 0, /* tp_hash */
167 0, /* tp_hash */
136 0, /* tp_call */
168 0, /* tp_call */
137 0, /* tp_str */
169 0, /* tp_str */
138 0, /* tp_getattro */
170 0, /* tp_getattro */
139 0, /* tp_setattro */
171 0, /* tp_setattro */
140 0, /* tp_as_buffer */
172 0, /* tp_as_buffer */
141 Py_TPFLAGS_DEFAULT, /* tp_flags */
173 Py_TPFLAGS_DEFAULT, /* tp_flags */
142 "dirstate tuple", /* tp_doc */
174 "dirstate tuple", /* tp_doc */
143 0, /* tp_traverse */
175 0, /* tp_traverse */
144 0, /* tp_clear */
176 0, /* tp_clear */
145 0, /* tp_richcompare */
177 0, /* tp_richcompare */
146 0, /* tp_weaklistoffset */
178 0, /* tp_weaklistoffset */
147 0, /* tp_iter */
179 0, /* tp_iter */
148 0, /* tp_iternext */
180 0, /* tp_iternext */
149 0, /* tp_methods */
181 dirstatetuple_methods, /* tp_methods */
150 0, /* tp_members */
182 0, /* tp_members */
151 0, /* tp_getset */
183 0, /* tp_getset */
152 0, /* tp_base */
184 0, /* tp_base */
153 0, /* tp_dict */
185 0, /* tp_dict */
154 0, /* tp_descr_get */
186 0, /* tp_descr_get */
155 0, /* tp_descr_set */
187 0, /* tp_descr_set */
156 0, /* tp_dictoffset */
188 0, /* tp_dictoffset */
157 0, /* tp_init */
189 0, /* tp_init */
158 0, /* tp_alloc */
190 0, /* tp_alloc */
159 dirstate_tuple_new, /* tp_new */
191 dirstate_tuple_new, /* tp_new */
160 };
192 };
161
193
162 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
194 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
163 {
195 {
164 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
196 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
165 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
197 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
166 char state, *cur, *str, *cpos;
198 char state, *cur, *str, *cpos;
167 int mode, size, mtime;
199 int mode, size, mtime;
168 unsigned int flen, pos = 40;
200 unsigned int flen, pos = 40;
169 Py_ssize_t len = 40;
201 Py_ssize_t len = 40;
170 Py_ssize_t readlen;
202 Py_ssize_t readlen;
171
203
172 if (!PyArg_ParseTuple(
204 if (!PyArg_ParseTuple(
173 args, PY23("O!O!s#:parse_dirstate", "O!O!y#:parse_dirstate"),
205 args, PY23("O!O!s#:parse_dirstate", "O!O!y#:parse_dirstate"),
174 &PyDict_Type, &dmap, &PyDict_Type, &cmap, &str, &readlen)) {
206 &PyDict_Type, &dmap, &PyDict_Type, &cmap, &str, &readlen)) {
175 goto quit;
207 goto quit;
176 }
208 }
177
209
178 len = readlen;
210 len = readlen;
179
211
180 /* read parents */
212 /* read parents */
181 if (len < 40) {
213 if (len < 40) {
182 PyErr_SetString(PyExc_ValueError,
214 PyErr_SetString(PyExc_ValueError,
183 "too little data for parents");
215 "too little data for parents");
184 goto quit;
216 goto quit;
185 }
217 }
186
218
187 parents = Py_BuildValue(PY23("s#s#", "y#y#"), str, (Py_ssize_t)20,
219 parents = Py_BuildValue(PY23("s#s#", "y#y#"), str, (Py_ssize_t)20,
188 str + 20, (Py_ssize_t)20);
220 str + 20, (Py_ssize_t)20);
189 if (!parents) {
221 if (!parents) {
190 goto quit;
222 goto quit;
191 }
223 }
192
224
193 /* read filenames */
225 /* read filenames */
194 while (pos >= 40 && pos < len) {
226 while (pos >= 40 && pos < len) {
195 if (pos + 17 > len) {
227 if (pos + 17 > len) {
196 PyErr_SetString(PyExc_ValueError,
228 PyErr_SetString(PyExc_ValueError,
197 "overflow in dirstate");
229 "overflow in dirstate");
198 goto quit;
230 goto quit;
199 }
231 }
200 cur = str + pos;
232 cur = str + pos;
201 /* unpack header */
233 /* unpack header */
202 state = *cur;
234 state = *cur;
203 mode = getbe32(cur + 1);
235 mode = getbe32(cur + 1);
204 size = getbe32(cur + 5);
236 size = getbe32(cur + 5);
205 mtime = getbe32(cur + 9);
237 mtime = getbe32(cur + 9);
206 flen = getbe32(cur + 13);
238 flen = getbe32(cur + 13);
207 pos += 17;
239 pos += 17;
208 cur += 17;
240 cur += 17;
209 if (flen > len - pos) {
241 if (flen > len - pos) {
210 PyErr_SetString(PyExc_ValueError,
242 PyErr_SetString(PyExc_ValueError,
211 "overflow in dirstate");
243 "overflow in dirstate");
212 goto quit;
244 goto quit;
213 }
245 }
214
246
215 entry =
247 entry =
216 (PyObject *)make_dirstate_tuple(state, mode, size, mtime);
248 (PyObject *)make_dirstate_tuple(state, mode, size, mtime);
217 cpos = memchr(cur, 0, flen);
249 cpos = memchr(cur, 0, flen);
218 if (cpos) {
250 if (cpos) {
219 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
251 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
220 cname = PyBytes_FromStringAndSize(
252 cname = PyBytes_FromStringAndSize(
221 cpos + 1, flen - (cpos - cur) - 1);
253 cpos + 1, flen - (cpos - cur) - 1);
222 if (!fname || !cname ||
254 if (!fname || !cname ||
223 PyDict_SetItem(cmap, fname, cname) == -1 ||
255 PyDict_SetItem(cmap, fname, cname) == -1 ||
224 PyDict_SetItem(dmap, fname, entry) == -1) {
256 PyDict_SetItem(dmap, fname, entry) == -1) {
225 goto quit;
257 goto quit;
226 }
258 }
227 Py_DECREF(cname);
259 Py_DECREF(cname);
228 } else {
260 } else {
229 fname = PyBytes_FromStringAndSize(cur, flen);
261 fname = PyBytes_FromStringAndSize(cur, flen);
230 if (!fname ||
262 if (!fname ||
231 PyDict_SetItem(dmap, fname, entry) == -1) {
263 PyDict_SetItem(dmap, fname, entry) == -1) {
232 goto quit;
264 goto quit;
233 }
265 }
234 }
266 }
235 Py_DECREF(fname);
267 Py_DECREF(fname);
236 Py_DECREF(entry);
268 Py_DECREF(entry);
237 fname = cname = entry = NULL;
269 fname = cname = entry = NULL;
238 pos += flen;
270 pos += flen;
239 }
271 }
240
272
241 ret = parents;
273 ret = parents;
242 Py_INCREF(ret);
274 Py_INCREF(ret);
243 quit:
275 quit:
244 Py_XDECREF(fname);
276 Py_XDECREF(fname);
245 Py_XDECREF(cname);
277 Py_XDECREF(cname);
246 Py_XDECREF(entry);
278 Py_XDECREF(entry);
247 Py_XDECREF(parents);
279 Py_XDECREF(parents);
248 return ret;
280 return ret;
249 }
281 }
250
282
251 /*
283 /*
252 * Build a set of non-normal and other parent entries from the dirstate dmap
284 * Build a set of non-normal and other parent entries from the dirstate dmap
253 */
285 */
254 static PyObject *nonnormalotherparententries(PyObject *self, PyObject *args)
286 static PyObject *nonnormalotherparententries(PyObject *self, PyObject *args)
255 {
287 {
256 PyObject *dmap, *fname, *v;
288 PyObject *dmap, *fname, *v;
257 PyObject *nonnset = NULL, *otherpset = NULL, *result = NULL;
289 PyObject *nonnset = NULL, *otherpset = NULL, *result = NULL;
258 Py_ssize_t pos;
290 Py_ssize_t pos;
259
291
260 if (!PyArg_ParseTuple(args, "O!:nonnormalentries", &PyDict_Type,
292 if (!PyArg_ParseTuple(args, "O!:nonnormalentries", &PyDict_Type,
261 &dmap)) {
293 &dmap)) {
262 goto bail;
294 goto bail;
263 }
295 }
264
296
265 nonnset = PySet_New(NULL);
297 nonnset = PySet_New(NULL);
266 if (nonnset == NULL) {
298 if (nonnset == NULL) {
267 goto bail;
299 goto bail;
268 }
300 }
269
301
270 otherpset = PySet_New(NULL);
302 otherpset = PySet_New(NULL);
271 if (otherpset == NULL) {
303 if (otherpset == NULL) {
272 goto bail;
304 goto bail;
273 }
305 }
274
306
275 pos = 0;
307 pos = 0;
276 while (PyDict_Next(dmap, &pos, &fname, &v)) {
308 while (PyDict_Next(dmap, &pos, &fname, &v)) {
277 dirstateTupleObject *t;
309 dirstateTupleObject *t;
278 if (!dirstate_tuple_check(v)) {
310 if (!dirstate_tuple_check(v)) {
279 PyErr_SetString(PyExc_TypeError,
311 PyErr_SetString(PyExc_TypeError,
280 "expected a dirstate tuple");
312 "expected a dirstate tuple");
281 goto bail;
313 goto bail;
282 }
314 }
283 t = (dirstateTupleObject *)v;
315 t = (dirstateTupleObject *)v;
284
316
285 if (t->state == 'n' && t->size == -2) {
317 if (t->state == 'n' && t->size == -2) {
286 if (PySet_Add(otherpset, fname) == -1) {
318 if (PySet_Add(otherpset, fname) == -1) {
287 goto bail;
319 goto bail;
288 }
320 }
289 }
321 }
290
322
291 if (t->state == 'n' && t->mtime != -1) {
323 if (t->state == 'n' && t->mtime != -1) {
292 continue;
324 continue;
293 }
325 }
294 if (PySet_Add(nonnset, fname) == -1) {
326 if (PySet_Add(nonnset, fname) == -1) {
295 goto bail;
327 goto bail;
296 }
328 }
297 }
329 }
298
330
299 result = Py_BuildValue("(OO)", nonnset, otherpset);
331 result = Py_BuildValue("(OO)", nonnset, otherpset);
300 if (result == NULL) {
332 if (result == NULL) {
301 goto bail;
333 goto bail;
302 }
334 }
303 Py_DECREF(nonnset);
335 Py_DECREF(nonnset);
304 Py_DECREF(otherpset);
336 Py_DECREF(otherpset);
305 return result;
337 return result;
306 bail:
338 bail:
307 Py_XDECREF(nonnset);
339 Py_XDECREF(nonnset);
308 Py_XDECREF(otherpset);
340 Py_XDECREF(otherpset);
309 Py_XDECREF(result);
341 Py_XDECREF(result);
310 return NULL;
342 return NULL;
311 }
343 }
312
344
313 /*
345 /*
314 * Efficiently pack a dirstate object into its on-disk format.
346 * Efficiently pack a dirstate object into its on-disk format.
315 */
347 */
316 static PyObject *pack_dirstate(PyObject *self, PyObject *args)
348 static PyObject *pack_dirstate(PyObject *self, PyObject *args)
317 {
349 {
318 PyObject *packobj = NULL;
350 PyObject *packobj = NULL;
319 PyObject *map, *copymap, *pl, *mtime_unset = NULL;
351 PyObject *map, *copymap, *pl, *mtime_unset = NULL;
320 Py_ssize_t nbytes, pos, l;
352 Py_ssize_t nbytes, pos, l;
321 PyObject *k, *v = NULL, *pn;
353 PyObject *k, *v = NULL, *pn;
322 char *p, *s;
354 char *p, *s;
323 int now;
355 int now;
324
356
325 if (!PyArg_ParseTuple(args, "O!O!O!i:pack_dirstate", &PyDict_Type, &map,
357 if (!PyArg_ParseTuple(args, "O!O!O!i:pack_dirstate", &PyDict_Type, &map,
326 &PyDict_Type, &copymap, &PyTuple_Type, &pl,
358 &PyDict_Type, &copymap, &PyTuple_Type, &pl,
327 &now)) {
359 &now)) {
328 return NULL;
360 return NULL;
329 }
361 }
330
362
331 if (PyTuple_Size(pl) != 2) {
363 if (PyTuple_Size(pl) != 2) {
332 PyErr_SetString(PyExc_TypeError, "expected 2-element tuple");
364 PyErr_SetString(PyExc_TypeError, "expected 2-element tuple");
333 return NULL;
365 return NULL;
334 }
366 }
335
367
336 /* Figure out how much we need to allocate. */
368 /* Figure out how much we need to allocate. */
337 for (nbytes = 40, pos = 0; PyDict_Next(map, &pos, &k, &v);) {
369 for (nbytes = 40, pos = 0; PyDict_Next(map, &pos, &k, &v);) {
338 PyObject *c;
370 PyObject *c;
339 if (!PyBytes_Check(k)) {
371 if (!PyBytes_Check(k)) {
340 PyErr_SetString(PyExc_TypeError, "expected string key");
372 PyErr_SetString(PyExc_TypeError, "expected string key");
341 goto bail;
373 goto bail;
342 }
374 }
343 nbytes += PyBytes_GET_SIZE(k) + 17;
375 nbytes += PyBytes_GET_SIZE(k) + 17;
344 c = PyDict_GetItem(copymap, k);
376 c = PyDict_GetItem(copymap, k);
345 if (c) {
377 if (c) {
346 if (!PyBytes_Check(c)) {
378 if (!PyBytes_Check(c)) {
347 PyErr_SetString(PyExc_TypeError,
379 PyErr_SetString(PyExc_TypeError,
348 "expected string key");
380 "expected string key");
349 goto bail;
381 goto bail;
350 }
382 }
351 nbytes += PyBytes_GET_SIZE(c) + 1;
383 nbytes += PyBytes_GET_SIZE(c) + 1;
352 }
384 }
353 }
385 }
354
386
355 packobj = PyBytes_FromStringAndSize(NULL, nbytes);
387 packobj = PyBytes_FromStringAndSize(NULL, nbytes);
356 if (packobj == NULL) {
388 if (packobj == NULL) {
357 goto bail;
389 goto bail;
358 }
390 }
359
391
360 p = PyBytes_AS_STRING(packobj);
392 p = PyBytes_AS_STRING(packobj);
361
393
362 pn = PyTuple_GET_ITEM(pl, 0);
394 pn = PyTuple_GET_ITEM(pl, 0);
363 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
395 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
364 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
396 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
365 goto bail;
397 goto bail;
366 }
398 }
367 memcpy(p, s, l);
399 memcpy(p, s, l);
368 p += 20;
400 p += 20;
369 pn = PyTuple_GET_ITEM(pl, 1);
401 pn = PyTuple_GET_ITEM(pl, 1);
370 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
402 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
371 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
403 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
372 goto bail;
404 goto bail;
373 }
405 }
374 memcpy(p, s, l);
406 memcpy(p, s, l);
375 p += 20;
407 p += 20;
376
408
377 for (pos = 0; PyDict_Next(map, &pos, &k, &v);) {
409 for (pos = 0; PyDict_Next(map, &pos, &k, &v);) {
378 dirstateTupleObject *tuple;
410 dirstateTupleObject *tuple;
379 char state;
411 char state;
380 int mode, size, mtime;
412 int mode, size, mtime;
381 Py_ssize_t len, l;
413 Py_ssize_t len, l;
382 PyObject *o;
414 PyObject *o;
383 char *t;
415 char *t;
384
416
385 if (!dirstate_tuple_check(v)) {
417 if (!dirstate_tuple_check(v)) {
386 PyErr_SetString(PyExc_TypeError,
418 PyErr_SetString(PyExc_TypeError,
387 "expected a dirstate tuple");
419 "expected a dirstate tuple");
388 goto bail;
420 goto bail;
389 }
421 }
390 tuple = (dirstateTupleObject *)v;
422 tuple = (dirstateTupleObject *)v;
391
423
392 state = tuple->state;
424 state = tuple->state;
393 mode = tuple->mode;
425 mode = tuple->mode;
394 size = tuple->size;
426 size = tuple->size;
395 mtime = tuple->mtime;
427 mtime = tuple->mtime;
396 if (state == 'n' && mtime == now) {
428 if (state == 'n' && mtime == now) {
397 /* See pure/parsers.py:pack_dirstate for why we do
429 /* See pure/parsers.py:pack_dirstate for why we do
398 * this. */
430 * this. */
399 mtime = -1;
431 mtime = -1;
400 mtime_unset = (PyObject *)make_dirstate_tuple(
432 mtime_unset = (PyObject *)make_dirstate_tuple(
401 state, mode, size, mtime);
433 state, mode, size, mtime);
402 if (!mtime_unset) {
434 if (!mtime_unset) {
403 goto bail;
435 goto bail;
404 }
436 }
405 if (PyDict_SetItem(map, k, mtime_unset) == -1) {
437 if (PyDict_SetItem(map, k, mtime_unset) == -1) {
406 goto bail;
438 goto bail;
407 }
439 }
408 Py_DECREF(mtime_unset);
440 Py_DECREF(mtime_unset);
409 mtime_unset = NULL;
441 mtime_unset = NULL;
410 }
442 }
411 *p++ = state;
443 *p++ = state;
412 putbe32((uint32_t)mode, p);
444 putbe32((uint32_t)mode, p);
413 putbe32((uint32_t)size, p + 4);
445 putbe32((uint32_t)size, p + 4);
414 putbe32((uint32_t)mtime, p + 8);
446 putbe32((uint32_t)mtime, p + 8);
415 t = p + 12;
447 t = p + 12;
416 p += 16;
448 p += 16;
417 len = PyBytes_GET_SIZE(k);
449 len = PyBytes_GET_SIZE(k);
418 memcpy(p, PyBytes_AS_STRING(k), len);
450 memcpy(p, PyBytes_AS_STRING(k), len);
419 p += len;
451 p += len;
420 o = PyDict_GetItem(copymap, k);
452 o = PyDict_GetItem(copymap, k);
421 if (o) {
453 if (o) {
422 *p++ = '\0';
454 *p++ = '\0';
423 l = PyBytes_GET_SIZE(o);
455 l = PyBytes_GET_SIZE(o);
424 memcpy(p, PyBytes_AS_STRING(o), l);
456 memcpy(p, PyBytes_AS_STRING(o), l);
425 p += l;
457 p += l;
426 len += l + 1;
458 len += l + 1;
427 }
459 }
428 putbe32((uint32_t)len, t);
460 putbe32((uint32_t)len, t);
429 }
461 }
430
462
431 pos = p - PyBytes_AS_STRING(packobj);
463 pos = p - PyBytes_AS_STRING(packobj);
432 if (pos != nbytes) {
464 if (pos != nbytes) {
433 PyErr_Format(PyExc_SystemError, "bad dirstate size: %ld != %ld",
465 PyErr_Format(PyExc_SystemError, "bad dirstate size: %ld != %ld",
434 (long)pos, (long)nbytes);
466 (long)pos, (long)nbytes);
435 goto bail;
467 goto bail;
436 }
468 }
437
469
438 return packobj;
470 return packobj;
439 bail:
471 bail:
440 Py_XDECREF(mtime_unset);
472 Py_XDECREF(mtime_unset);
441 Py_XDECREF(packobj);
473 Py_XDECREF(packobj);
442 Py_XDECREF(v);
474 Py_XDECREF(v);
443 return NULL;
475 return NULL;
444 }
476 }
445
477
446 #define BUMPED_FIX 1
478 #define BUMPED_FIX 1
447 #define USING_SHA_256 2
479 #define USING_SHA_256 2
448 #define FM1_HEADER_SIZE (4 + 8 + 2 + 2 + 1 + 1 + 1)
480 #define FM1_HEADER_SIZE (4 + 8 + 2 + 2 + 1 + 1 + 1)
449
481
450 static PyObject *readshas(const char *source, unsigned char num,
482 static PyObject *readshas(const char *source, unsigned char num,
451 Py_ssize_t hashwidth)
483 Py_ssize_t hashwidth)
452 {
484 {
453 int i;
485 int i;
454 PyObject *list = PyTuple_New(num);
486 PyObject *list = PyTuple_New(num);
455 if (list == NULL) {
487 if (list == NULL) {
456 return NULL;
488 return NULL;
457 }
489 }
458 for (i = 0; i < num; i++) {
490 for (i = 0; i < num; i++) {
459 PyObject *hash = PyBytes_FromStringAndSize(source, hashwidth);
491 PyObject *hash = PyBytes_FromStringAndSize(source, hashwidth);
460 if (hash == NULL) {
492 if (hash == NULL) {
461 Py_DECREF(list);
493 Py_DECREF(list);
462 return NULL;
494 return NULL;
463 }
495 }
464 PyTuple_SET_ITEM(list, i, hash);
496 PyTuple_SET_ITEM(list, i, hash);
465 source += hashwidth;
497 source += hashwidth;
466 }
498 }
467 return list;
499 return list;
468 }
500 }
469
501
470 static PyObject *fm1readmarker(const char *databegin, const char *dataend,
502 static PyObject *fm1readmarker(const char *databegin, const char *dataend,
471 uint32_t *msize)
503 uint32_t *msize)
472 {
504 {
473 const char *data = databegin;
505 const char *data = databegin;
474 const char *meta;
506 const char *meta;
475
507
476 double mtime;
508 double mtime;
477 int16_t tz;
509 int16_t tz;
478 uint16_t flags;
510 uint16_t flags;
479 unsigned char nsuccs, nparents, nmetadata;
511 unsigned char nsuccs, nparents, nmetadata;
480 Py_ssize_t hashwidth = 20;
512 Py_ssize_t hashwidth = 20;
481
513
482 PyObject *prec = NULL, *parents = NULL, *succs = NULL;
514 PyObject *prec = NULL, *parents = NULL, *succs = NULL;
483 PyObject *metadata = NULL, *ret = NULL;
515 PyObject *metadata = NULL, *ret = NULL;
484 int i;
516 int i;
485
517
486 if (data + FM1_HEADER_SIZE > dataend) {
518 if (data + FM1_HEADER_SIZE > dataend) {
487 goto overflow;
519 goto overflow;
488 }
520 }
489
521
490 *msize = getbe32(data);
522 *msize = getbe32(data);
491 data += 4;
523 data += 4;
492 mtime = getbefloat64(data);
524 mtime = getbefloat64(data);
493 data += 8;
525 data += 8;
494 tz = getbeint16(data);
526 tz = getbeint16(data);
495 data += 2;
527 data += 2;
496 flags = getbeuint16(data);
528 flags = getbeuint16(data);
497 data += 2;
529 data += 2;
498
530
499 if (flags & USING_SHA_256) {
531 if (flags & USING_SHA_256) {
500 hashwidth = 32;
532 hashwidth = 32;
501 }
533 }
502
534
503 nsuccs = (unsigned char)(*data++);
535 nsuccs = (unsigned char)(*data++);
504 nparents = (unsigned char)(*data++);
536 nparents = (unsigned char)(*data++);
505 nmetadata = (unsigned char)(*data++);
537 nmetadata = (unsigned char)(*data++);
506
538
507 if (databegin + *msize > dataend) {
539 if (databegin + *msize > dataend) {
508 goto overflow;
540 goto overflow;
509 }
541 }
510 dataend = databegin + *msize; /* narrow down to marker size */
542 dataend = databegin + *msize; /* narrow down to marker size */
511
543
512 if (data + hashwidth > dataend) {
544 if (data + hashwidth > dataend) {
513 goto overflow;
545 goto overflow;
514 }
546 }
515 prec = PyBytes_FromStringAndSize(data, hashwidth);
547 prec = PyBytes_FromStringAndSize(data, hashwidth);
516 data += hashwidth;
548 data += hashwidth;
517 if (prec == NULL) {
549 if (prec == NULL) {
518 goto bail;
550 goto bail;
519 }
551 }
520
552
521 if (data + nsuccs * hashwidth > dataend) {
553 if (data + nsuccs * hashwidth > dataend) {
522 goto overflow;
554 goto overflow;
523 }
555 }
524 succs = readshas(data, nsuccs, hashwidth);
556 succs = readshas(data, nsuccs, hashwidth);
525 if (succs == NULL) {
557 if (succs == NULL) {
526 goto bail;
558 goto bail;
527 }
559 }
528 data += nsuccs * hashwidth;
560 data += nsuccs * hashwidth;
529
561
530 if (nparents == 1 || nparents == 2) {
562 if (nparents == 1 || nparents == 2) {
531 if (data + nparents * hashwidth > dataend) {
563 if (data + nparents * hashwidth > dataend) {
532 goto overflow;
564 goto overflow;
533 }
565 }
534 parents = readshas(data, nparents, hashwidth);
566 parents = readshas(data, nparents, hashwidth);
535 if (parents == NULL) {
567 if (parents == NULL) {
536 goto bail;
568 goto bail;
537 }
569 }
538 data += nparents * hashwidth;
570 data += nparents * hashwidth;
539 } else {
571 } else {
540 parents = Py_None;
572 parents = Py_None;
541 Py_INCREF(parents);
573 Py_INCREF(parents);
542 }
574 }
543
575
544 if (data + 2 * nmetadata > dataend) {
576 if (data + 2 * nmetadata > dataend) {
545 goto overflow;
577 goto overflow;
546 }
578 }
547 meta = data + (2 * nmetadata);
579 meta = data + (2 * nmetadata);
548 metadata = PyTuple_New(nmetadata);
580 metadata = PyTuple_New(nmetadata);
549 if (metadata == NULL) {
581 if (metadata == NULL) {
550 goto bail;
582 goto bail;
551 }
583 }
552 for (i = 0; i < nmetadata; i++) {
584 for (i = 0; i < nmetadata; i++) {
553 PyObject *tmp, *left = NULL, *right = NULL;
585 PyObject *tmp, *left = NULL, *right = NULL;
554 Py_ssize_t leftsize = (unsigned char)(*data++);
586 Py_ssize_t leftsize = (unsigned char)(*data++);
555 Py_ssize_t rightsize = (unsigned char)(*data++);
587 Py_ssize_t rightsize = (unsigned char)(*data++);
556 if (meta + leftsize + rightsize > dataend) {
588 if (meta + leftsize + rightsize > dataend) {
557 goto overflow;
589 goto overflow;
558 }
590 }
559 left = PyBytes_FromStringAndSize(meta, leftsize);
591 left = PyBytes_FromStringAndSize(meta, leftsize);
560 meta += leftsize;
592 meta += leftsize;
561 right = PyBytes_FromStringAndSize(meta, rightsize);
593 right = PyBytes_FromStringAndSize(meta, rightsize);
562 meta += rightsize;
594 meta += rightsize;
563 tmp = PyTuple_New(2);
595 tmp = PyTuple_New(2);
564 if (!left || !right || !tmp) {
596 if (!left || !right || !tmp) {
565 Py_XDECREF(left);
597 Py_XDECREF(left);
566 Py_XDECREF(right);
598 Py_XDECREF(right);
567 Py_XDECREF(tmp);
599 Py_XDECREF(tmp);
568 goto bail;
600 goto bail;
569 }
601 }
570 PyTuple_SET_ITEM(tmp, 0, left);
602 PyTuple_SET_ITEM(tmp, 0, left);
571 PyTuple_SET_ITEM(tmp, 1, right);
603 PyTuple_SET_ITEM(tmp, 1, right);
572 PyTuple_SET_ITEM(metadata, i, tmp);
604 PyTuple_SET_ITEM(metadata, i, tmp);
573 }
605 }
574 ret = Py_BuildValue("(OOHO(di)O)", prec, succs, flags, metadata, mtime,
606 ret = Py_BuildValue("(OOHO(di)O)", prec, succs, flags, metadata, mtime,
575 (int)tz * 60, parents);
607 (int)tz * 60, parents);
576 goto bail; /* return successfully */
608 goto bail; /* return successfully */
577
609
578 overflow:
610 overflow:
579 PyErr_SetString(PyExc_ValueError, "overflow in obsstore");
611 PyErr_SetString(PyExc_ValueError, "overflow in obsstore");
580 bail:
612 bail:
581 Py_XDECREF(prec);
613 Py_XDECREF(prec);
582 Py_XDECREF(succs);
614 Py_XDECREF(succs);
583 Py_XDECREF(metadata);
615 Py_XDECREF(metadata);
584 Py_XDECREF(parents);
616 Py_XDECREF(parents);
585 return ret;
617 return ret;
586 }
618 }
587
619
588 static PyObject *fm1readmarkers(PyObject *self, PyObject *args)
620 static PyObject *fm1readmarkers(PyObject *self, PyObject *args)
589 {
621 {
590 const char *data, *dataend;
622 const char *data, *dataend;
591 Py_ssize_t datalen, offset, stop;
623 Py_ssize_t datalen, offset, stop;
592 PyObject *markers = NULL;
624 PyObject *markers = NULL;
593
625
594 if (!PyArg_ParseTuple(args, PY23("s#nn", "y#nn"), &data, &datalen,
626 if (!PyArg_ParseTuple(args, PY23("s#nn", "y#nn"), &data, &datalen,
595 &offset, &stop)) {
627 &offset, &stop)) {
596 return NULL;
628 return NULL;
597 }
629 }
598 if (offset < 0) {
630 if (offset < 0) {
599 PyErr_SetString(PyExc_ValueError,
631 PyErr_SetString(PyExc_ValueError,
600 "invalid negative offset in fm1readmarkers");
632 "invalid negative offset in fm1readmarkers");
601 return NULL;
633 return NULL;
602 }
634 }
603 if (stop > datalen) {
635 if (stop > datalen) {
604 PyErr_SetString(
636 PyErr_SetString(
605 PyExc_ValueError,
637 PyExc_ValueError,
606 "stop longer than data length in fm1readmarkers");
638 "stop longer than data length in fm1readmarkers");
607 return NULL;
639 return NULL;
608 }
640 }
609 dataend = data + datalen;
641 dataend = data + datalen;
610 data += offset;
642 data += offset;
611 markers = PyList_New(0);
643 markers = PyList_New(0);
612 if (!markers) {
644 if (!markers) {
613 return NULL;
645 return NULL;
614 }
646 }
615 while (offset < stop) {
647 while (offset < stop) {
616 uint32_t msize;
648 uint32_t msize;
617 int error;
649 int error;
618 PyObject *record = fm1readmarker(data, dataend, &msize);
650 PyObject *record = fm1readmarker(data, dataend, &msize);
619 if (!record) {
651 if (!record) {
620 goto bail;
652 goto bail;
621 }
653 }
622 error = PyList_Append(markers, record);
654 error = PyList_Append(markers, record);
623 Py_DECREF(record);
655 Py_DECREF(record);
624 if (error) {
656 if (error) {
625 goto bail;
657 goto bail;
626 }
658 }
627 data += msize;
659 data += msize;
628 offset += msize;
660 offset += msize;
629 }
661 }
630 return markers;
662 return markers;
631 bail:
663 bail:
632 Py_DECREF(markers);
664 Py_DECREF(markers);
633 return NULL;
665 return NULL;
634 }
666 }
635
667
636 static char parsers_doc[] = "Efficient content parsing.";
668 static char parsers_doc[] = "Efficient content parsing.";
637
669
638 PyObject *encodedir(PyObject *self, PyObject *args);
670 PyObject *encodedir(PyObject *self, PyObject *args);
639 PyObject *pathencode(PyObject *self, PyObject *args);
671 PyObject *pathencode(PyObject *self, PyObject *args);
640 PyObject *lowerencode(PyObject *self, PyObject *args);
672 PyObject *lowerencode(PyObject *self, PyObject *args);
641 PyObject *parse_index2(PyObject *self, PyObject *args, PyObject *kwargs);
673 PyObject *parse_index2(PyObject *self, PyObject *args, PyObject *kwargs);
642
674
643 static PyMethodDef methods[] = {
675 static PyMethodDef methods[] = {
644 {"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"},
676 {"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"},
645 {"nonnormalotherparententries", nonnormalotherparententries, METH_VARARGS,
677 {"nonnormalotherparententries", nonnormalotherparententries, METH_VARARGS,
646 "create a set containing non-normal and other parent entries of given "
678 "create a set containing non-normal and other parent entries of given "
647 "dirstate\n"},
679 "dirstate\n"},
648 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
680 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
649 {"parse_index2", (PyCFunction)parse_index2, METH_VARARGS | METH_KEYWORDS,
681 {"parse_index2", (PyCFunction)parse_index2, METH_VARARGS | METH_KEYWORDS,
650 "parse a revlog index\n"},
682 "parse a revlog index\n"},
651 {"isasciistr", isasciistr, METH_VARARGS, "check if an ASCII string\n"},
683 {"isasciistr", isasciistr, METH_VARARGS, "check if an ASCII string\n"},
652 {"asciilower", asciilower, METH_VARARGS, "lowercase an ASCII string\n"},
684 {"asciilower", asciilower, METH_VARARGS, "lowercase an ASCII string\n"},
653 {"asciiupper", asciiupper, METH_VARARGS, "uppercase an ASCII string\n"},
685 {"asciiupper", asciiupper, METH_VARARGS, "uppercase an ASCII string\n"},
654 {"dict_new_presized", dict_new_presized, METH_VARARGS,
686 {"dict_new_presized", dict_new_presized, METH_VARARGS,
655 "construct a dict with an expected size\n"},
687 "construct a dict with an expected size\n"},
656 {"make_file_foldmap", make_file_foldmap, METH_VARARGS,
688 {"make_file_foldmap", make_file_foldmap, METH_VARARGS,
657 "make file foldmap\n"},
689 "make file foldmap\n"},
658 {"jsonescapeu8fast", jsonescapeu8fast, METH_VARARGS,
690 {"jsonescapeu8fast", jsonescapeu8fast, METH_VARARGS,
659 "escape a UTF-8 byte string to JSON (fast path)\n"},
691 "escape a UTF-8 byte string to JSON (fast path)\n"},
660 {"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"},
692 {"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"},
661 {"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"},
693 {"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"},
662 {"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"},
694 {"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"},
663 {"fm1readmarkers", fm1readmarkers, METH_VARARGS,
695 {"fm1readmarkers", fm1readmarkers, METH_VARARGS,
664 "parse v1 obsolete markers\n"},
696 "parse v1 obsolete markers\n"},
665 {NULL, NULL}};
697 {NULL, NULL}};
666
698
667 void dirs_module_init(PyObject *mod);
699 void dirs_module_init(PyObject *mod);
668 void manifest_module_init(PyObject *mod);
700 void manifest_module_init(PyObject *mod);
669 void revlog_module_init(PyObject *mod);
701 void revlog_module_init(PyObject *mod);
670
702
671 static const int version = 20;
703 static const int version = 20;
672
704
673 static void module_init(PyObject *mod)
705 static void module_init(PyObject *mod)
674 {
706 {
675 PyObject *capsule = NULL;
707 PyObject *capsule = NULL;
676 PyModule_AddIntConstant(mod, "version", version);
708 PyModule_AddIntConstant(mod, "version", version);
677
709
678 /* This module constant has two purposes. First, it lets us unit test
710 /* This module constant has two purposes. First, it lets us unit test
679 * the ImportError raised without hard-coding any error text. This
711 * the ImportError raised without hard-coding any error text. This
680 * means we can change the text in the future without breaking tests,
712 * means we can change the text in the future without breaking tests,
681 * even across changesets without a recompile. Second, its presence
713 * even across changesets without a recompile. Second, its presence
682 * can be used to determine whether the version-checking logic is
714 * can be used to determine whether the version-checking logic is
683 * present, which also helps in testing across changesets without a
715 * present, which also helps in testing across changesets without a
684 * recompile. Note that this means the pure-Python version of parsers
716 * recompile. Note that this means the pure-Python version of parsers
685 * should not have this module constant. */
717 * should not have this module constant. */
686 PyModule_AddStringConstant(mod, "versionerrortext", versionerrortext);
718 PyModule_AddStringConstant(mod, "versionerrortext", versionerrortext);
687
719
688 dirs_module_init(mod);
720 dirs_module_init(mod);
689 manifest_module_init(mod);
721 manifest_module_init(mod);
690 revlog_module_init(mod);
722 revlog_module_init(mod);
691
723
692 capsule = PyCapsule_New(
724 capsule = PyCapsule_New(
693 make_dirstate_tuple,
725 make_dirstate_tuple,
694 "mercurial.cext.parsers.make_dirstate_tuple_CAPI", NULL);
726 "mercurial.cext.parsers.make_dirstate_tuple_CAPI", NULL);
695 if (capsule != NULL)
727 if (capsule != NULL)
696 PyModule_AddObject(mod, "make_dirstate_tuple_CAPI", capsule);
728 PyModule_AddObject(mod, "make_dirstate_tuple_CAPI", capsule);
697
729
698 if (PyType_Ready(&dirstateTupleType) < 0) {
730 if (PyType_Ready(&dirstateTupleType) < 0) {
699 return;
731 return;
700 }
732 }
701 Py_INCREF(&dirstateTupleType);
733 Py_INCREF(&dirstateTupleType);
702 PyModule_AddObject(mod, "dirstatetuple",
734 PyModule_AddObject(mod, "dirstatetuple",
703 (PyObject *)&dirstateTupleType);
735 (PyObject *)&dirstateTupleType);
704 }
736 }
705
737
706 static int check_python_version(void)
738 static int check_python_version(void)
707 {
739 {
708 PyObject *sys = PyImport_ImportModule("sys"), *ver;
740 PyObject *sys = PyImport_ImportModule("sys"), *ver;
709 long hexversion;
741 long hexversion;
710 if (!sys) {
742 if (!sys) {
711 return -1;
743 return -1;
712 }
744 }
713 ver = PyObject_GetAttrString(sys, "hexversion");
745 ver = PyObject_GetAttrString(sys, "hexversion");
714 Py_DECREF(sys);
746 Py_DECREF(sys);
715 if (!ver) {
747 if (!ver) {
716 return -1;
748 return -1;
717 }
749 }
718 hexversion = PyInt_AsLong(ver);
750 hexversion = PyInt_AsLong(ver);
719 Py_DECREF(ver);
751 Py_DECREF(ver);
720 /* sys.hexversion is a 32-bit number by default, so the -1 case
752 /* sys.hexversion is a 32-bit number by default, so the -1 case
721 * should only occur in unusual circumstances (e.g. if sys.hexversion
753 * should only occur in unusual circumstances (e.g. if sys.hexversion
722 * is manually set to an invalid value). */
754 * is manually set to an invalid value). */
723 if ((hexversion == -1) || (hexversion >> 16 != PY_VERSION_HEX >> 16)) {
755 if ((hexversion == -1) || (hexversion >> 16 != PY_VERSION_HEX >> 16)) {
724 PyErr_Format(PyExc_ImportError,
756 PyErr_Format(PyExc_ImportError,
725 "%s: The Mercurial extension "
757 "%s: The Mercurial extension "
726 "modules were compiled with Python " PY_VERSION
758 "modules were compiled with Python " PY_VERSION
727 ", but "
759 ", but "
728 "Mercurial is currently using Python with "
760 "Mercurial is currently using Python with "
729 "sys.hexversion=%ld: "
761 "sys.hexversion=%ld: "
730 "Python %s\n at: %s",
762 "Python %s\n at: %s",
731 versionerrortext, hexversion, Py_GetVersion(),
763 versionerrortext, hexversion, Py_GetVersion(),
732 Py_GetProgramFullPath());
764 Py_GetProgramFullPath());
733 return -1;
765 return -1;
734 }
766 }
735 return 0;
767 return 0;
736 }
768 }
737
769
738 #ifdef IS_PY3K
770 #ifdef IS_PY3K
739 static struct PyModuleDef parsers_module = {PyModuleDef_HEAD_INIT, "parsers",
771 static struct PyModuleDef parsers_module = {PyModuleDef_HEAD_INIT, "parsers",
740 parsers_doc, -1, methods};
772 parsers_doc, -1, methods};
741
773
742 PyMODINIT_FUNC PyInit_parsers(void)
774 PyMODINIT_FUNC PyInit_parsers(void)
743 {
775 {
744 PyObject *mod;
776 PyObject *mod;
745
777
746 if (check_python_version() == -1)
778 if (check_python_version() == -1)
747 return NULL;
779 return NULL;
748 mod = PyModule_Create(&parsers_module);
780 mod = PyModule_Create(&parsers_module);
749 module_init(mod);
781 module_init(mod);
750 return mod;
782 return mod;
751 }
783 }
752 #else
784 #else
753 PyMODINIT_FUNC initparsers(void)
785 PyMODINIT_FUNC initparsers(void)
754 {
786 {
755 PyObject *mod;
787 PyObject *mod;
756
788
757 if (check_python_version() == -1) {
789 if (check_python_version() == -1) {
758 return;
790 return;
759 }
791 }
760 mod = Py_InitModule3("parsers", methods, parsers_doc);
792 mod = Py_InitModule3("parsers", methods, parsers_doc);
761 module_init(mod);
793 module_init(mod);
762 }
794 }
763 #endif
795 #endif
@@ -1,456 +1,479 b''
1 # parsers.py - Python implementation of parsers.c
1 # parsers.py - Python implementation of parsers.c
2 #
2 #
3 # Copyright 2009 Olivia Mackall <olivia@selenic.com> and others
3 # Copyright 2009 Olivia Mackall <olivia@selenic.com> and others
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import struct
10 import struct
11 import zlib
11 import zlib
12
12
13 from ..node import (
13 from ..node import (
14 nullrev,
14 nullrev,
15 sha1nodeconstants,
15 sha1nodeconstants,
16 )
16 )
17 from .. import (
17 from .. import (
18 error,
18 error,
19 pycompat,
19 pycompat,
20 revlogutils,
20 revlogutils,
21 util,
21 util,
22 )
22 )
23
23
24 from ..revlogutils import nodemap as nodemaputil
24 from ..revlogutils import nodemap as nodemaputil
25 from ..revlogutils import constants as revlog_constants
25 from ..revlogutils import constants as revlog_constants
26
26
27 stringio = pycompat.bytesio
27 stringio = pycompat.bytesio
28
28
29
29
30 _pack = struct.pack
30 _pack = struct.pack
31 _unpack = struct.unpack
31 _unpack = struct.unpack
32 _compress = zlib.compress
32 _compress = zlib.compress
33 _decompress = zlib.decompress
33 _decompress = zlib.decompress
34
34
35
35
36 class dirstatetuple(object):
36 class dirstatetuple(object):
37 """represent a dirstate entry
37 """represent a dirstate entry
38
38
39 It contains:
39 It contains:
40
40
41 - state (one of 'n', 'a', 'r', 'm')
41 - state (one of 'n', 'a', 'r', 'm')
42 - mode,
42 - mode,
43 - size,
43 - size,
44 - mtime,
44 - mtime,
45 """
45 """
46
46
47 __slot__ = ('_state', '_mode', '_size', '_mtime')
47 __slot__ = ('_state', '_mode', '_size', '_mtime')
48
48
49 def __init__(self, state, mode, size, mtime):
49 def __init__(self, state, mode, size, mtime):
50 self._state = state
50 self._state = state
51 self._mode = mode
51 self._mode = mode
52 self._size = size
52 self._size = size
53 self._mtime = mtime
53 self._mtime = mtime
54
54
55 def __getitem__(self, idx):
55 def __getitem__(self, idx):
56 if idx == 0 or idx == -4:
56 if idx == 0 or idx == -4:
57 return self._state
57 return self._state
58 elif idx == 1 or idx == -3:
58 elif idx == 1 or idx == -3:
59 return self._mode
59 return self._mode
60 elif idx == 2 or idx == -2:
60 elif idx == 2 or idx == -2:
61 return self._size
61 return self._size
62 elif idx == 3 or idx == -1:
62 elif idx == 3 or idx == -1:
63 return self._mtime
63 return self._mtime
64 else:
64 else:
65 raise IndexError(idx)
65 raise IndexError(idx)
66
66
67 def v1_state(self):
68 """return a "state" suitable for v1 serialization"""
69 return self._state
70
71 def v1_mode(self):
72 """return a "mode" suitable for v1 serialization"""
73 return self._mode
74
75 def v1_size(self):
76 """return a "size" suitable for v1 serialization"""
77 return self._size
78
79 def v1_mtime(self):
80 """return a "mtime" suitable for v1 serialization"""
81 return self._mtime
82
67
83
68 def gettype(q):
84 def gettype(q):
69 return int(q & 0xFFFF)
85 return int(q & 0xFFFF)
70
86
71
87
72 class BaseIndexObject(object):
88 class BaseIndexObject(object):
73 # Can I be passed to an algorithme implemented in Rust ?
89 # Can I be passed to an algorithme implemented in Rust ?
74 rust_ext_compat = 0
90 rust_ext_compat = 0
75 # Format of an index entry according to Python's `struct` language
91 # Format of an index entry according to Python's `struct` language
76 index_format = revlog_constants.INDEX_ENTRY_V1
92 index_format = revlog_constants.INDEX_ENTRY_V1
77 # Size of a C unsigned long long int, platform independent
93 # Size of a C unsigned long long int, platform independent
78 big_int_size = struct.calcsize(b'>Q')
94 big_int_size = struct.calcsize(b'>Q')
79 # Size of a C long int, platform independent
95 # Size of a C long int, platform independent
80 int_size = struct.calcsize(b'>i')
96 int_size = struct.calcsize(b'>i')
81 # An empty index entry, used as a default value to be overridden, or nullrev
97 # An empty index entry, used as a default value to be overridden, or nullrev
82 null_item = (
98 null_item = (
83 0,
99 0,
84 0,
100 0,
85 0,
101 0,
86 -1,
102 -1,
87 -1,
103 -1,
88 -1,
104 -1,
89 -1,
105 -1,
90 sha1nodeconstants.nullid,
106 sha1nodeconstants.nullid,
91 0,
107 0,
92 0,
108 0,
93 revlog_constants.COMP_MODE_INLINE,
109 revlog_constants.COMP_MODE_INLINE,
94 revlog_constants.COMP_MODE_INLINE,
110 revlog_constants.COMP_MODE_INLINE,
95 )
111 )
96
112
97 @util.propertycache
113 @util.propertycache
98 def entry_size(self):
114 def entry_size(self):
99 return self.index_format.size
115 return self.index_format.size
100
116
101 @property
117 @property
102 def nodemap(self):
118 def nodemap(self):
103 msg = b"index.nodemap is deprecated, use index.[has_node|rev|get_rev]"
119 msg = b"index.nodemap is deprecated, use index.[has_node|rev|get_rev]"
104 util.nouideprecwarn(msg, b'5.3', stacklevel=2)
120 util.nouideprecwarn(msg, b'5.3', stacklevel=2)
105 return self._nodemap
121 return self._nodemap
106
122
107 @util.propertycache
123 @util.propertycache
108 def _nodemap(self):
124 def _nodemap(self):
109 nodemap = nodemaputil.NodeMap({sha1nodeconstants.nullid: nullrev})
125 nodemap = nodemaputil.NodeMap({sha1nodeconstants.nullid: nullrev})
110 for r in range(0, len(self)):
126 for r in range(0, len(self)):
111 n = self[r][7]
127 n = self[r][7]
112 nodemap[n] = r
128 nodemap[n] = r
113 return nodemap
129 return nodemap
114
130
115 def has_node(self, node):
131 def has_node(self, node):
116 """return True if the node exist in the index"""
132 """return True if the node exist in the index"""
117 return node in self._nodemap
133 return node in self._nodemap
118
134
119 def rev(self, node):
135 def rev(self, node):
120 """return a revision for a node
136 """return a revision for a node
121
137
122 If the node is unknown, raise a RevlogError"""
138 If the node is unknown, raise a RevlogError"""
123 return self._nodemap[node]
139 return self._nodemap[node]
124
140
125 def get_rev(self, node):
141 def get_rev(self, node):
126 """return a revision for a node
142 """return a revision for a node
127
143
128 If the node is unknown, return None"""
144 If the node is unknown, return None"""
129 return self._nodemap.get(node)
145 return self._nodemap.get(node)
130
146
131 def _stripnodes(self, start):
147 def _stripnodes(self, start):
132 if '_nodemap' in vars(self):
148 if '_nodemap' in vars(self):
133 for r in range(start, len(self)):
149 for r in range(start, len(self)):
134 n = self[r][7]
150 n = self[r][7]
135 del self._nodemap[n]
151 del self._nodemap[n]
136
152
137 def clearcaches(self):
153 def clearcaches(self):
138 self.__dict__.pop('_nodemap', None)
154 self.__dict__.pop('_nodemap', None)
139
155
140 def __len__(self):
156 def __len__(self):
141 return self._lgt + len(self._extra)
157 return self._lgt + len(self._extra)
142
158
143 def append(self, tup):
159 def append(self, tup):
144 if '_nodemap' in vars(self):
160 if '_nodemap' in vars(self):
145 self._nodemap[tup[7]] = len(self)
161 self._nodemap[tup[7]] = len(self)
146 data = self._pack_entry(len(self), tup)
162 data = self._pack_entry(len(self), tup)
147 self._extra.append(data)
163 self._extra.append(data)
148
164
149 def _pack_entry(self, rev, entry):
165 def _pack_entry(self, rev, entry):
150 assert entry[8] == 0
166 assert entry[8] == 0
151 assert entry[9] == 0
167 assert entry[9] == 0
152 return self.index_format.pack(*entry[:8])
168 return self.index_format.pack(*entry[:8])
153
169
154 def _check_index(self, i):
170 def _check_index(self, i):
155 if not isinstance(i, int):
171 if not isinstance(i, int):
156 raise TypeError(b"expecting int indexes")
172 raise TypeError(b"expecting int indexes")
157 if i < 0 or i >= len(self):
173 if i < 0 or i >= len(self):
158 raise IndexError
174 raise IndexError
159
175
160 def __getitem__(self, i):
176 def __getitem__(self, i):
161 if i == -1:
177 if i == -1:
162 return self.null_item
178 return self.null_item
163 self._check_index(i)
179 self._check_index(i)
164 if i >= self._lgt:
180 if i >= self._lgt:
165 data = self._extra[i - self._lgt]
181 data = self._extra[i - self._lgt]
166 else:
182 else:
167 index = self._calculate_index(i)
183 index = self._calculate_index(i)
168 data = self._data[index : index + self.entry_size]
184 data = self._data[index : index + self.entry_size]
169 r = self._unpack_entry(i, data)
185 r = self._unpack_entry(i, data)
170 if self._lgt and i == 0:
186 if self._lgt and i == 0:
171 offset = revlogutils.offset_type(0, gettype(r[0]))
187 offset = revlogutils.offset_type(0, gettype(r[0]))
172 r = (offset,) + r[1:]
188 r = (offset,) + r[1:]
173 return r
189 return r
174
190
175 def _unpack_entry(self, rev, data):
191 def _unpack_entry(self, rev, data):
176 r = self.index_format.unpack(data)
192 r = self.index_format.unpack(data)
177 r = r + (
193 r = r + (
178 0,
194 0,
179 0,
195 0,
180 revlog_constants.COMP_MODE_INLINE,
196 revlog_constants.COMP_MODE_INLINE,
181 revlog_constants.COMP_MODE_INLINE,
197 revlog_constants.COMP_MODE_INLINE,
182 )
198 )
183 return r
199 return r
184
200
185 def pack_header(self, header):
201 def pack_header(self, header):
186 """pack header information as binary"""
202 """pack header information as binary"""
187 v_fmt = revlog_constants.INDEX_HEADER
203 v_fmt = revlog_constants.INDEX_HEADER
188 return v_fmt.pack(header)
204 return v_fmt.pack(header)
189
205
190 def entry_binary(self, rev):
206 def entry_binary(self, rev):
191 """return the raw binary string representing a revision"""
207 """return the raw binary string representing a revision"""
192 entry = self[rev]
208 entry = self[rev]
193 p = revlog_constants.INDEX_ENTRY_V1.pack(*entry[:8])
209 p = revlog_constants.INDEX_ENTRY_V1.pack(*entry[:8])
194 if rev == 0:
210 if rev == 0:
195 p = p[revlog_constants.INDEX_HEADER.size :]
211 p = p[revlog_constants.INDEX_HEADER.size :]
196 return p
212 return p
197
213
198
214
199 class IndexObject(BaseIndexObject):
215 class IndexObject(BaseIndexObject):
200 def __init__(self, data):
216 def __init__(self, data):
201 assert len(data) % self.entry_size == 0, (
217 assert len(data) % self.entry_size == 0, (
202 len(data),
218 len(data),
203 self.entry_size,
219 self.entry_size,
204 len(data) % self.entry_size,
220 len(data) % self.entry_size,
205 )
221 )
206 self._data = data
222 self._data = data
207 self._lgt = len(data) // self.entry_size
223 self._lgt = len(data) // self.entry_size
208 self._extra = []
224 self._extra = []
209
225
210 def _calculate_index(self, i):
226 def _calculate_index(self, i):
211 return i * self.entry_size
227 return i * self.entry_size
212
228
213 def __delitem__(self, i):
229 def __delitem__(self, i):
214 if not isinstance(i, slice) or not i.stop == -1 or i.step is not None:
230 if not isinstance(i, slice) or not i.stop == -1 or i.step is not None:
215 raise ValueError(b"deleting slices only supports a:-1 with step 1")
231 raise ValueError(b"deleting slices only supports a:-1 with step 1")
216 i = i.start
232 i = i.start
217 self._check_index(i)
233 self._check_index(i)
218 self._stripnodes(i)
234 self._stripnodes(i)
219 if i < self._lgt:
235 if i < self._lgt:
220 self._data = self._data[: i * self.entry_size]
236 self._data = self._data[: i * self.entry_size]
221 self._lgt = i
237 self._lgt = i
222 self._extra = []
238 self._extra = []
223 else:
239 else:
224 self._extra = self._extra[: i - self._lgt]
240 self._extra = self._extra[: i - self._lgt]
225
241
226
242
227 class PersistentNodeMapIndexObject(IndexObject):
243 class PersistentNodeMapIndexObject(IndexObject):
228 """a Debug oriented class to test persistent nodemap
244 """a Debug oriented class to test persistent nodemap
229
245
230 We need a simple python object to test API and higher level behavior. See
246 We need a simple python object to test API and higher level behavior. See
231 the Rust implementation for more serious usage. This should be used only
247 the Rust implementation for more serious usage. This should be used only
232 through the dedicated `devel.persistent-nodemap` config.
248 through the dedicated `devel.persistent-nodemap` config.
233 """
249 """
234
250
235 def nodemap_data_all(self):
251 def nodemap_data_all(self):
236 """Return bytes containing a full serialization of a nodemap
252 """Return bytes containing a full serialization of a nodemap
237
253
238 The nodemap should be valid for the full set of revisions in the
254 The nodemap should be valid for the full set of revisions in the
239 index."""
255 index."""
240 return nodemaputil.persistent_data(self)
256 return nodemaputil.persistent_data(self)
241
257
242 def nodemap_data_incremental(self):
258 def nodemap_data_incremental(self):
243 """Return bytes containing a incremental update to persistent nodemap
259 """Return bytes containing a incremental update to persistent nodemap
244
260
245 This containst the data for an append-only update of the data provided
261 This containst the data for an append-only update of the data provided
246 in the last call to `update_nodemap_data`.
262 in the last call to `update_nodemap_data`.
247 """
263 """
248 if self._nm_root is None:
264 if self._nm_root is None:
249 return None
265 return None
250 docket = self._nm_docket
266 docket = self._nm_docket
251 changed, data = nodemaputil.update_persistent_data(
267 changed, data = nodemaputil.update_persistent_data(
252 self, self._nm_root, self._nm_max_idx, self._nm_docket.tip_rev
268 self, self._nm_root, self._nm_max_idx, self._nm_docket.tip_rev
253 )
269 )
254
270
255 self._nm_root = self._nm_max_idx = self._nm_docket = None
271 self._nm_root = self._nm_max_idx = self._nm_docket = None
256 return docket, changed, data
272 return docket, changed, data
257
273
258 def update_nodemap_data(self, docket, nm_data):
274 def update_nodemap_data(self, docket, nm_data):
259 """provide full block of persisted binary data for a nodemap
275 """provide full block of persisted binary data for a nodemap
260
276
261 The data are expected to come from disk. See `nodemap_data_all` for a
277 The data are expected to come from disk. See `nodemap_data_all` for a
262 produceur of such data."""
278 produceur of such data."""
263 if nm_data is not None:
279 if nm_data is not None:
264 self._nm_root, self._nm_max_idx = nodemaputil.parse_data(nm_data)
280 self._nm_root, self._nm_max_idx = nodemaputil.parse_data(nm_data)
265 if self._nm_root:
281 if self._nm_root:
266 self._nm_docket = docket
282 self._nm_docket = docket
267 else:
283 else:
268 self._nm_root = self._nm_max_idx = self._nm_docket = None
284 self._nm_root = self._nm_max_idx = self._nm_docket = None
269
285
270
286
271 class InlinedIndexObject(BaseIndexObject):
287 class InlinedIndexObject(BaseIndexObject):
272 def __init__(self, data, inline=0):
288 def __init__(self, data, inline=0):
273 self._data = data
289 self._data = data
274 self._lgt = self._inline_scan(None)
290 self._lgt = self._inline_scan(None)
275 self._inline_scan(self._lgt)
291 self._inline_scan(self._lgt)
276 self._extra = []
292 self._extra = []
277
293
278 def _inline_scan(self, lgt):
294 def _inline_scan(self, lgt):
279 off = 0
295 off = 0
280 if lgt is not None:
296 if lgt is not None:
281 self._offsets = [0] * lgt
297 self._offsets = [0] * lgt
282 count = 0
298 count = 0
283 while off <= len(self._data) - self.entry_size:
299 while off <= len(self._data) - self.entry_size:
284 start = off + self.big_int_size
300 start = off + self.big_int_size
285 (s,) = struct.unpack(
301 (s,) = struct.unpack(
286 b'>i',
302 b'>i',
287 self._data[start : start + self.int_size],
303 self._data[start : start + self.int_size],
288 )
304 )
289 if lgt is not None:
305 if lgt is not None:
290 self._offsets[count] = off
306 self._offsets[count] = off
291 count += 1
307 count += 1
292 off += self.entry_size + s
308 off += self.entry_size + s
293 if off != len(self._data):
309 if off != len(self._data):
294 raise ValueError(b"corrupted data")
310 raise ValueError(b"corrupted data")
295 return count
311 return count
296
312
297 def __delitem__(self, i):
313 def __delitem__(self, i):
298 if not isinstance(i, slice) or not i.stop == -1 or i.step is not None:
314 if not isinstance(i, slice) or not i.stop == -1 or i.step is not None:
299 raise ValueError(b"deleting slices only supports a:-1 with step 1")
315 raise ValueError(b"deleting slices only supports a:-1 with step 1")
300 i = i.start
316 i = i.start
301 self._check_index(i)
317 self._check_index(i)
302 self._stripnodes(i)
318 self._stripnodes(i)
303 if i < self._lgt:
319 if i < self._lgt:
304 self._offsets = self._offsets[:i]
320 self._offsets = self._offsets[:i]
305 self._lgt = i
321 self._lgt = i
306 self._extra = []
322 self._extra = []
307 else:
323 else:
308 self._extra = self._extra[: i - self._lgt]
324 self._extra = self._extra[: i - self._lgt]
309
325
310 def _calculate_index(self, i):
326 def _calculate_index(self, i):
311 return self._offsets[i]
327 return self._offsets[i]
312
328
313
329
314 def parse_index2(data, inline, revlogv2=False):
330 def parse_index2(data, inline, revlogv2=False):
315 if not inline:
331 if not inline:
316 cls = IndexObject2 if revlogv2 else IndexObject
332 cls = IndexObject2 if revlogv2 else IndexObject
317 return cls(data), None
333 return cls(data), None
318 cls = InlinedIndexObject
334 cls = InlinedIndexObject
319 return cls(data, inline), (0, data)
335 return cls(data, inline), (0, data)
320
336
321
337
322 def parse_index_cl_v2(data):
338 def parse_index_cl_v2(data):
323 return IndexChangelogV2(data), None
339 return IndexChangelogV2(data), None
324
340
325
341
326 class IndexObject2(IndexObject):
342 class IndexObject2(IndexObject):
327 index_format = revlog_constants.INDEX_ENTRY_V2
343 index_format = revlog_constants.INDEX_ENTRY_V2
328
344
329 def replace_sidedata_info(
345 def replace_sidedata_info(
330 self,
346 self,
331 rev,
347 rev,
332 sidedata_offset,
348 sidedata_offset,
333 sidedata_length,
349 sidedata_length,
334 offset_flags,
350 offset_flags,
335 compression_mode,
351 compression_mode,
336 ):
352 ):
337 """
353 """
338 Replace an existing index entry's sidedata offset and length with new
354 Replace an existing index entry's sidedata offset and length with new
339 ones.
355 ones.
340 This cannot be used outside of the context of sidedata rewriting,
356 This cannot be used outside of the context of sidedata rewriting,
341 inside the transaction that creates the revision `rev`.
357 inside the transaction that creates the revision `rev`.
342 """
358 """
343 if rev < 0:
359 if rev < 0:
344 raise KeyError
360 raise KeyError
345 self._check_index(rev)
361 self._check_index(rev)
346 if rev < self._lgt:
362 if rev < self._lgt:
347 msg = b"cannot rewrite entries outside of this transaction"
363 msg = b"cannot rewrite entries outside of this transaction"
348 raise KeyError(msg)
364 raise KeyError(msg)
349 else:
365 else:
350 entry = list(self[rev])
366 entry = list(self[rev])
351 entry[0] = offset_flags
367 entry[0] = offset_flags
352 entry[8] = sidedata_offset
368 entry[8] = sidedata_offset
353 entry[9] = sidedata_length
369 entry[9] = sidedata_length
354 entry[11] = compression_mode
370 entry[11] = compression_mode
355 entry = tuple(entry)
371 entry = tuple(entry)
356 new = self._pack_entry(rev, entry)
372 new = self._pack_entry(rev, entry)
357 self._extra[rev - self._lgt] = new
373 self._extra[rev - self._lgt] = new
358
374
359 def _unpack_entry(self, rev, data):
375 def _unpack_entry(self, rev, data):
360 data = self.index_format.unpack(data)
376 data = self.index_format.unpack(data)
361 entry = data[:10]
377 entry = data[:10]
362 data_comp = data[10] & 3
378 data_comp = data[10] & 3
363 sidedata_comp = (data[10] & (3 << 2)) >> 2
379 sidedata_comp = (data[10] & (3 << 2)) >> 2
364 return entry + (data_comp, sidedata_comp)
380 return entry + (data_comp, sidedata_comp)
365
381
366 def _pack_entry(self, rev, entry):
382 def _pack_entry(self, rev, entry):
367 data = entry[:10]
383 data = entry[:10]
368 data_comp = entry[10] & 3
384 data_comp = entry[10] & 3
369 sidedata_comp = (entry[11] & 3) << 2
385 sidedata_comp = (entry[11] & 3) << 2
370 data += (data_comp | sidedata_comp,)
386 data += (data_comp | sidedata_comp,)
371
387
372 return self.index_format.pack(*data)
388 return self.index_format.pack(*data)
373
389
374 def entry_binary(self, rev):
390 def entry_binary(self, rev):
375 """return the raw binary string representing a revision"""
391 """return the raw binary string representing a revision"""
376 entry = self[rev]
392 entry = self[rev]
377 return self._pack_entry(rev, entry)
393 return self._pack_entry(rev, entry)
378
394
379 def pack_header(self, header):
395 def pack_header(self, header):
380 """pack header information as binary"""
396 """pack header information as binary"""
381 msg = 'version header should go in the docket, not the index: %d'
397 msg = 'version header should go in the docket, not the index: %d'
382 msg %= header
398 msg %= header
383 raise error.ProgrammingError(msg)
399 raise error.ProgrammingError(msg)
384
400
385
401
386 class IndexChangelogV2(IndexObject2):
402 class IndexChangelogV2(IndexObject2):
387 index_format = revlog_constants.INDEX_ENTRY_CL_V2
403 index_format = revlog_constants.INDEX_ENTRY_CL_V2
388
404
389 def _unpack_entry(self, rev, data, r=True):
405 def _unpack_entry(self, rev, data, r=True):
390 items = self.index_format.unpack(data)
406 items = self.index_format.unpack(data)
391 entry = items[:3] + (rev, rev) + items[3:8]
407 entry = items[:3] + (rev, rev) + items[3:8]
392 data_comp = items[8] & 3
408 data_comp = items[8] & 3
393 sidedata_comp = (items[8] >> 2) & 3
409 sidedata_comp = (items[8] >> 2) & 3
394 return entry + (data_comp, sidedata_comp)
410 return entry + (data_comp, sidedata_comp)
395
411
396 def _pack_entry(self, rev, entry):
412 def _pack_entry(self, rev, entry):
397 assert entry[3] == rev, entry[3]
413 assert entry[3] == rev, entry[3]
398 assert entry[4] == rev, entry[4]
414 assert entry[4] == rev, entry[4]
399 data = entry[:3] + entry[5:10]
415 data = entry[:3] + entry[5:10]
400 data_comp = entry[10] & 3
416 data_comp = entry[10] & 3
401 sidedata_comp = (entry[11] & 3) << 2
417 sidedata_comp = (entry[11] & 3) << 2
402 data += (data_comp | sidedata_comp,)
418 data += (data_comp | sidedata_comp,)
403 return self.index_format.pack(*data)
419 return self.index_format.pack(*data)
404
420
405
421
406 def parse_index_devel_nodemap(data, inline):
422 def parse_index_devel_nodemap(data, inline):
407 """like parse_index2, but alway return a PersistentNodeMapIndexObject"""
423 """like parse_index2, but alway return a PersistentNodeMapIndexObject"""
408 return PersistentNodeMapIndexObject(data), None
424 return PersistentNodeMapIndexObject(data), None
409
425
410
426
411 def parse_dirstate(dmap, copymap, st):
427 def parse_dirstate(dmap, copymap, st):
412 parents = [st[:20], st[20:40]]
428 parents = [st[:20], st[20:40]]
413 # dereference fields so they will be local in loop
429 # dereference fields so they will be local in loop
414 format = b">cllll"
430 format = b">cllll"
415 e_size = struct.calcsize(format)
431 e_size = struct.calcsize(format)
416 pos1 = 40
432 pos1 = 40
417 l = len(st)
433 l = len(st)
418
434
419 # the inner loop
435 # the inner loop
420 while pos1 < l:
436 while pos1 < l:
421 pos2 = pos1 + e_size
437 pos2 = pos1 + e_size
422 e = _unpack(b">cllll", st[pos1:pos2]) # a literal here is faster
438 e = _unpack(b">cllll", st[pos1:pos2]) # a literal here is faster
423 pos1 = pos2 + e[4]
439 pos1 = pos2 + e[4]
424 f = st[pos2:pos1]
440 f = st[pos2:pos1]
425 if b'\0' in f:
441 if b'\0' in f:
426 f, c = f.split(b'\0')
442 f, c = f.split(b'\0')
427 copymap[f] = c
443 copymap[f] = c
428 dmap[f] = dirstatetuple(*e[:4])
444 dmap[f] = dirstatetuple(*e[:4])
429 return parents
445 return parents
430
446
431
447
432 def pack_dirstate(dmap, copymap, pl, now):
448 def pack_dirstate(dmap, copymap, pl, now):
433 now = int(now)
449 now = int(now)
434 cs = stringio()
450 cs = stringio()
435 write = cs.write
451 write = cs.write
436 write(b"".join(pl))
452 write(b"".join(pl))
437 for f, e in pycompat.iteritems(dmap):
453 for f, e in pycompat.iteritems(dmap):
438 if e[0] == b'n' and e[3] == now:
454 if e[0] == b'n' and e[3] == now:
439 # The file was last modified "simultaneously" with the current
455 # The file was last modified "simultaneously" with the current
440 # write to dirstate (i.e. within the same second for file-
456 # write to dirstate (i.e. within the same second for file-
441 # systems with a granularity of 1 sec). This commonly happens
457 # systems with a granularity of 1 sec). This commonly happens
442 # for at least a couple of files on 'update'.
458 # for at least a couple of files on 'update'.
443 # The user could change the file without changing its size
459 # The user could change the file without changing its size
444 # within the same second. Invalidate the file's mtime in
460 # within the same second. Invalidate the file's mtime in
445 # dirstate, forcing future 'status' calls to compare the
461 # dirstate, forcing future 'status' calls to compare the
446 # contents of the file if the size is the same. This prevents
462 # contents of the file if the size is the same. This prevents
447 # mistakenly treating such files as clean.
463 # mistakenly treating such files as clean.
448 e = dirstatetuple(e[0], e[1], e[2], -1)
464 e = dirstatetuple(e[0], e[1], e[2], -1)
449 dmap[f] = e
465 dmap[f] = e
450
466
451 if f in copymap:
467 if f in copymap:
452 f = b"%s\0%s" % (f, copymap[f])
468 f = b"%s\0%s" % (f, copymap[f])
453 e = _pack(b">cllll", e[0], e[1], e[2], e[3], len(f))
469 e = _pack(
470 b">cllll",
471 e.v1_state(),
472 e.v1_mode(),
473 e.v1_size(),
474 e.v1_mtime(),
475 len(f),
476 )
454 write(e)
477 write(e)
455 write(f)
478 write(f)
456 return cs.getvalue()
479 return cs.getvalue()
General Comments 0
You need to be logged in to leave comments. Login now