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