##// END OF EJS Templates
dirstate: Remove the `state == ' '` special case...
Simon Sapin -
r48837:631f6b44 default
parent child Browse files
Show More
@@ -1,1333 +1,1320 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 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 static const int ambiguous_time = -1;
34 static const int ambiguous_time = -1;
35
35
36 static PyObject *dict_new_presized(PyObject *self, PyObject *args)
36 static PyObject *dict_new_presized(PyObject *self, PyObject *args)
37 {
37 {
38 Py_ssize_t expected_size;
38 Py_ssize_t expected_size;
39
39
40 if (!PyArg_ParseTuple(args, "n:make_presized_dict", &expected_size)) {
40 if (!PyArg_ParseTuple(args, "n:make_presized_dict", &expected_size)) {
41 return NULL;
41 return NULL;
42 }
42 }
43
43
44 return _dict_new_presized(expected_size);
44 return _dict_new_presized(expected_size);
45 }
45 }
46
46
47 static PyObject *dirstate_item_new(PyTypeObject *subtype, PyObject *args,
47 static PyObject *dirstate_item_new(PyTypeObject *subtype, PyObject *args,
48 PyObject *kwds)
48 PyObject *kwds)
49 {
49 {
50 /* We do all the initialization here and not a tp_init function because
50 /* We do all the initialization here and not a tp_init function because
51 * dirstate_item is immutable. */
51 * dirstate_item is immutable. */
52 dirstateItemObject *t;
52 dirstateItemObject *t;
53 int wc_tracked;
53 int wc_tracked;
54 int p1_tracked;
54 int p1_tracked;
55 int p2_tracked;
55 int p2_tracked;
56 int merged;
56 int merged;
57 int clean_p1;
57 int clean_p1;
58 int clean_p2;
58 int clean_p2;
59 int possibly_dirty;
59 int possibly_dirty;
60 PyObject *parentfiledata;
60 PyObject *parentfiledata;
61 static char *keywords_name[] = {
61 static char *keywords_name[] = {
62 "wc_tracked", "p1_tracked", "p2_tracked",
62 "wc_tracked", "p1_tracked", "p2_tracked",
63 "merged", "clean_p1", "clean_p2",
63 "merged", "clean_p1", "clean_p2",
64 "possibly_dirty", "parentfiledata", NULL,
64 "possibly_dirty", "parentfiledata", NULL,
65 };
65 };
66 wc_tracked = 0;
66 wc_tracked = 0;
67 p1_tracked = 0;
67 p1_tracked = 0;
68 p2_tracked = 0;
68 p2_tracked = 0;
69 merged = 0;
69 merged = 0;
70 clean_p1 = 0;
70 clean_p1 = 0;
71 clean_p2 = 0;
71 clean_p2 = 0;
72 possibly_dirty = 0;
72 possibly_dirty = 0;
73 parentfiledata = Py_None;
73 parentfiledata = Py_None;
74 if (!PyArg_ParseTupleAndKeywords(args, kwds, "iiiiiiiO", keywords_name,
74 if (!PyArg_ParseTupleAndKeywords(args, kwds, "iiiiiiiO", keywords_name,
75 &wc_tracked, &p1_tracked, &p2_tracked,
75 &wc_tracked, &p1_tracked, &p2_tracked,
76 &merged, &clean_p1, &clean_p2,
76 &merged, &clean_p1, &clean_p2,
77 &possibly_dirty, &parentfiledata
77 &possibly_dirty, &parentfiledata
78
78
79 )) {
79 )) {
80 return NULL;
80 return NULL;
81 }
81 }
82 if (merged && (clean_p1 || clean_p2)) {
82 if (merged && (clean_p1 || clean_p2)) {
83 PyErr_SetString(PyExc_RuntimeError,
83 PyErr_SetString(PyExc_RuntimeError,
84 "`merged` argument incompatible with "
84 "`merged` argument incompatible with "
85 "`clean_p1`/`clean_p2`");
85 "`clean_p1`/`clean_p2`");
86 return NULL;
86 return NULL;
87 }
87 }
88 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
88 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
89 if (!t) {
89 if (!t) {
90 return NULL;
90 return NULL;
91 }
91 }
92
92
93 t->flags = 0;
93 t->flags = 0;
94 if (wc_tracked) {
94 if (wc_tracked) {
95 t->flags |= dirstate_flag_wc_tracked;
95 t->flags |= dirstate_flag_wc_tracked;
96 }
96 }
97 if (p1_tracked) {
97 if (p1_tracked) {
98 t->flags |= dirstate_flag_p1_tracked;
98 t->flags |= dirstate_flag_p1_tracked;
99 }
99 }
100 if (p2_tracked) {
100 if (p2_tracked) {
101 t->flags |= dirstate_flag_p2_tracked;
101 t->flags |= dirstate_flag_p2_tracked;
102 }
102 }
103 if (possibly_dirty) {
103 if (possibly_dirty) {
104 t->flags |= dirstate_flag_possibly_dirty;
104 t->flags |= dirstate_flag_possibly_dirty;
105 }
105 }
106 if (merged) {
106 if (merged) {
107 t->flags |= dirstate_flag_merged;
107 t->flags |= dirstate_flag_merged;
108 }
108 }
109 if (clean_p1) {
109 if (clean_p1) {
110 t->flags |= dirstate_flag_clean_p1;
110 t->flags |= dirstate_flag_clean_p1;
111 }
111 }
112 if (clean_p2) {
112 if (clean_p2) {
113 t->flags |= dirstate_flag_clean_p2;
113 t->flags |= dirstate_flag_clean_p2;
114 }
114 }
115 t->mode = 0;
115 t->mode = 0;
116 t->size = dirstate_v1_nonnormal;
116 t->size = dirstate_v1_nonnormal;
117 t->mtime = ambiguous_time;
117 t->mtime = ambiguous_time;
118 if (parentfiledata != Py_None) {
118 if (parentfiledata != Py_None) {
119 if (!PyTuple_CheckExact(parentfiledata)) {
119 if (!PyTuple_CheckExact(parentfiledata)) {
120 PyErr_SetString(
120 PyErr_SetString(
121 PyExc_TypeError,
121 PyExc_TypeError,
122 "parentfiledata should be a Tuple or None");
122 "parentfiledata should be a Tuple or None");
123 return NULL;
123 return NULL;
124 }
124 }
125 t->mode =
125 t->mode =
126 (int)PyLong_AsLong(PyTuple_GetItem(parentfiledata, 0));
126 (int)PyLong_AsLong(PyTuple_GetItem(parentfiledata, 0));
127 t->size =
127 t->size =
128 (int)PyLong_AsLong(PyTuple_GetItem(parentfiledata, 1));
128 (int)PyLong_AsLong(PyTuple_GetItem(parentfiledata, 1));
129 t->mtime =
129 t->mtime =
130 (int)PyLong_AsLong(PyTuple_GetItem(parentfiledata, 2));
130 (int)PyLong_AsLong(PyTuple_GetItem(parentfiledata, 2));
131 }
131 }
132 return (PyObject *)t;
132 return (PyObject *)t;
133 }
133 }
134
134
135 static void dirstate_item_dealloc(PyObject *o)
135 static void dirstate_item_dealloc(PyObject *o)
136 {
136 {
137 PyObject_Del(o);
137 PyObject_Del(o);
138 }
138 }
139
139
140 static inline bool dirstate_item_c_tracked(dirstateItemObject *self)
140 static inline bool dirstate_item_c_tracked(dirstateItemObject *self)
141 {
141 {
142 return (self->flags & dirstate_flag_wc_tracked);
142 return (self->flags & dirstate_flag_wc_tracked);
143 }
143 }
144
144
145 static inline bool dirstate_item_c_added(dirstateItemObject *self)
145 static inline bool dirstate_item_c_added(dirstateItemObject *self)
146 {
146 {
147 unsigned char mask =
147 unsigned char mask =
148 (dirstate_flag_wc_tracked | dirstate_flag_p1_tracked |
148 (dirstate_flag_wc_tracked | dirstate_flag_p1_tracked |
149 dirstate_flag_p2_tracked);
149 dirstate_flag_p2_tracked);
150 unsigned char target = dirstate_flag_wc_tracked;
150 unsigned char target = dirstate_flag_wc_tracked;
151 return (self->flags & mask) == target;
151 return (self->flags & mask) == target;
152 }
152 }
153
153
154 static inline bool dirstate_item_c_removed(dirstateItemObject *self)
154 static inline bool dirstate_item_c_removed(dirstateItemObject *self)
155 {
155 {
156 if (self->flags & dirstate_flag_wc_tracked) {
156 if (self->flags & dirstate_flag_wc_tracked) {
157 return false;
157 return false;
158 }
158 }
159 return (self->flags &
159 return (self->flags &
160 (dirstate_flag_p1_tracked | dirstate_flag_p2_tracked));
160 (dirstate_flag_p1_tracked | dirstate_flag_p2_tracked));
161 }
161 }
162
162
163 static inline bool dirstate_item_c_merged(dirstateItemObject *self)
163 static inline bool dirstate_item_c_merged(dirstateItemObject *self)
164 {
164 {
165 return ((self->flags & dirstate_flag_wc_tracked) &&
165 return ((self->flags & dirstate_flag_wc_tracked) &&
166 (self->flags & dirstate_flag_merged));
166 (self->flags & dirstate_flag_merged));
167 }
167 }
168
168
169 static inline bool dirstate_item_c_merged_removed(dirstateItemObject *self)
169 static inline bool dirstate_item_c_merged_removed(dirstateItemObject *self)
170 {
170 {
171 if (!dirstate_item_c_removed(self)) {
171 if (!dirstate_item_c_removed(self)) {
172 return false;
172 return false;
173 }
173 }
174 return (self->flags & dirstate_flag_merged);
174 return (self->flags & dirstate_flag_merged);
175 }
175 }
176
176
177 static inline bool dirstate_item_c_from_p2(dirstateItemObject *self)
177 static inline bool dirstate_item_c_from_p2(dirstateItemObject *self)
178 {
178 {
179 if (!dirstate_item_c_tracked(self)) {
179 if (!dirstate_item_c_tracked(self)) {
180 return false;
180 return false;
181 }
181 }
182 return (self->flags & dirstate_flag_clean_p2);
182 return (self->flags & dirstate_flag_clean_p2);
183 }
183 }
184
184
185 static inline bool dirstate_item_c_from_p2_removed(dirstateItemObject *self)
185 static inline bool dirstate_item_c_from_p2_removed(dirstateItemObject *self)
186 {
186 {
187 if (!dirstate_item_c_removed(self)) {
187 if (!dirstate_item_c_removed(self)) {
188 return false;
188 return false;
189 }
189 }
190 return (self->flags & dirstate_flag_clean_p2);
190 return (self->flags & dirstate_flag_clean_p2);
191 }
191 }
192
192
193 static inline char dirstate_item_c_v1_state(dirstateItemObject *self)
193 static inline char dirstate_item_c_v1_state(dirstateItemObject *self)
194 {
194 {
195 if (self->flags & dirstate_flag_rust_special) {
195 if (dirstate_item_c_removed(self)) {
196 return ' ';
197 } else if (dirstate_item_c_removed(self)) {
198 return 'r';
196 return 'r';
199 } else if (dirstate_item_c_merged(self)) {
197 } else if (dirstate_item_c_merged(self)) {
200 return 'm';
198 return 'm';
201 } else if (dirstate_item_c_added(self)) {
199 } else if (dirstate_item_c_added(self)) {
202 return 'a';
200 return 'a';
203 } else {
201 } else {
204 return 'n';
202 return 'n';
205 }
203 }
206 }
204 }
207
205
208 static inline int dirstate_item_c_v1_mode(dirstateItemObject *self)
206 static inline int dirstate_item_c_v1_mode(dirstateItemObject *self)
209 {
207 {
210 return self->mode;
208 return self->mode;
211 }
209 }
212
210
213 static inline int dirstate_item_c_v1_size(dirstateItemObject *self)
211 static inline int dirstate_item_c_v1_size(dirstateItemObject *self)
214 {
212 {
215 if (self->flags & dirstate_flag_rust_special) {
213 if (dirstate_item_c_merged_removed(self)) {
216 return self->size;
217 } else if (dirstate_item_c_merged_removed(self)) {
218 return dirstate_v1_nonnormal;
214 return dirstate_v1_nonnormal;
219 } else if (dirstate_item_c_from_p2_removed(self)) {
215 } else if (dirstate_item_c_from_p2_removed(self)) {
220 return dirstate_v1_from_p2;
216 return dirstate_v1_from_p2;
221 } else if (dirstate_item_c_removed(self)) {
217 } else if (dirstate_item_c_removed(self)) {
222 return 0;
218 return 0;
223 } else if (dirstate_item_c_merged(self)) {
219 } else if (dirstate_item_c_merged(self)) {
224 return dirstate_v1_from_p2;
220 return dirstate_v1_from_p2;
225 } else if (dirstate_item_c_added(self)) {
221 } else if (dirstate_item_c_added(self)) {
226 return dirstate_v1_nonnormal;
222 return dirstate_v1_nonnormal;
227 } else if (dirstate_item_c_from_p2(self)) {
223 } else if (dirstate_item_c_from_p2(self)) {
228 return dirstate_v1_from_p2;
224 return dirstate_v1_from_p2;
229 } else if (self->flags & dirstate_flag_possibly_dirty) {
225 } else if (self->flags & dirstate_flag_possibly_dirty) {
230 return self->size; /* NON NORMAL ? */
226 return self->size; /* NON NORMAL ? */
231 } else {
227 } else {
232 return self->size;
228 return self->size;
233 }
229 }
234 }
230 }
235
231
236 static inline int dirstate_item_c_v1_mtime(dirstateItemObject *self)
232 static inline int dirstate_item_c_v1_mtime(dirstateItemObject *self)
237 {
233 {
238 if (self->flags & dirstate_flag_rust_special) {
234 if (dirstate_item_c_removed(self)) {
239 return self->mtime;
240 } else if (dirstate_item_c_removed(self)) {
241 return 0;
235 return 0;
242 } else if (self->flags & dirstate_flag_possibly_dirty) {
236 } else if (self->flags & dirstate_flag_possibly_dirty) {
243 return ambiguous_time;
237 return ambiguous_time;
244 } else if (dirstate_item_c_merged(self)) {
238 } else if (dirstate_item_c_merged(self)) {
245 return ambiguous_time;
239 return ambiguous_time;
246 } else if (dirstate_item_c_added(self)) {
240 } else if (dirstate_item_c_added(self)) {
247 return ambiguous_time;
241 return ambiguous_time;
248 } else if (dirstate_item_c_from_p2(self)) {
242 } else if (dirstate_item_c_from_p2(self)) {
249 return ambiguous_time;
243 return ambiguous_time;
250 } else {
244 } else {
251 return self->mtime;
245 return self->mtime;
252 }
246 }
253 }
247 }
254
248
255 static PyObject *dirstate_item_v1_state(dirstateItemObject *self)
249 static PyObject *dirstate_item_v1_state(dirstateItemObject *self)
256 {
250 {
257 char state = dirstate_item_c_v1_state(self);
251 char state = dirstate_item_c_v1_state(self);
258 return PyBytes_FromStringAndSize(&state, 1);
252 return PyBytes_FromStringAndSize(&state, 1);
259 };
253 };
260
254
261 static PyObject *dirstate_item_v1_mode(dirstateItemObject *self)
255 static PyObject *dirstate_item_v1_mode(dirstateItemObject *self)
262 {
256 {
263 return PyInt_FromLong(dirstate_item_c_v1_mode(self));
257 return PyInt_FromLong(dirstate_item_c_v1_mode(self));
264 };
258 };
265
259
266 static PyObject *dirstate_item_v1_size(dirstateItemObject *self)
260 static PyObject *dirstate_item_v1_size(dirstateItemObject *self)
267 {
261 {
268 return PyInt_FromLong(dirstate_item_c_v1_size(self));
262 return PyInt_FromLong(dirstate_item_c_v1_size(self));
269 };
263 };
270
264
271 static PyObject *dirstate_item_v1_mtime(dirstateItemObject *self)
265 static PyObject *dirstate_item_v1_mtime(dirstateItemObject *self)
272 {
266 {
273 return PyInt_FromLong(dirstate_item_c_v1_mtime(self));
267 return PyInt_FromLong(dirstate_item_c_v1_mtime(self));
274 };
268 };
275
269
276 static PyObject *dirstate_item_need_delay(dirstateItemObject *self,
270 static PyObject *dirstate_item_need_delay(dirstateItemObject *self,
277 PyObject *value)
271 PyObject *value)
278 {
272 {
279 long now;
273 long now;
280 if (!pylong_to_long(value, &now)) {
274 if (!pylong_to_long(value, &now)) {
281 return NULL;
275 return NULL;
282 }
276 }
283 if (dirstate_item_c_v1_state(self) == 'n' &&
277 if (dirstate_item_c_v1_state(self) == 'n' &&
284 dirstate_item_c_v1_mtime(self) == now) {
278 dirstate_item_c_v1_mtime(self) == now) {
285 Py_RETURN_TRUE;
279 Py_RETURN_TRUE;
286 } else {
280 } else {
287 Py_RETURN_FALSE;
281 Py_RETURN_FALSE;
288 }
282 }
289 };
283 };
290
284
291 /* This will never change since it's bound to V1
285 /* This will never change since it's bound to V1
292 */
286 */
293 static inline dirstateItemObject *
287 static inline dirstateItemObject *
294 dirstate_item_from_v1_data(char state, int mode, int size, int mtime)
288 dirstate_item_from_v1_data(char state, int mode, int size, int mtime)
295 {
289 {
296 dirstateItemObject *t =
290 dirstateItemObject *t =
297 PyObject_New(dirstateItemObject, &dirstateItemType);
291 PyObject_New(dirstateItemObject, &dirstateItemType);
298 if (!t) {
292 if (!t) {
299 return NULL;
293 return NULL;
300 }
294 }
301
295
302 if (state == 'm') {
296 if (state == 'm') {
303 t->flags =
297 t->flags =
304 (dirstate_flag_wc_tracked | dirstate_flag_p1_tracked |
298 (dirstate_flag_wc_tracked | dirstate_flag_p1_tracked |
305 dirstate_flag_p2_tracked | dirstate_flag_merged);
299 dirstate_flag_p2_tracked | dirstate_flag_merged);
306 t->mode = 0;
300 t->mode = 0;
307 t->size = dirstate_v1_from_p2;
301 t->size = dirstate_v1_from_p2;
308 t->mtime = ambiguous_time;
302 t->mtime = ambiguous_time;
309 } else if (state == 'a') {
303 } else if (state == 'a') {
310 t->flags = dirstate_flag_wc_tracked;
304 t->flags = dirstate_flag_wc_tracked;
311 t->mode = 0;
305 t->mode = 0;
312 t->size = dirstate_v1_nonnormal;
306 t->size = dirstate_v1_nonnormal;
313 t->mtime = ambiguous_time;
307 t->mtime = ambiguous_time;
314 } else if (state == 'r') {
308 } else if (state == 'r') {
315 t->mode = 0;
309 t->mode = 0;
316 t->size = 0;
310 t->size = 0;
317 t->mtime = 0;
311 t->mtime = 0;
318 if (size == dirstate_v1_nonnormal) {
312 if (size == dirstate_v1_nonnormal) {
319 t->flags =
313 t->flags =
320 (dirstate_flag_p1_tracked |
314 (dirstate_flag_p1_tracked |
321 dirstate_flag_p2_tracked | dirstate_flag_merged);
315 dirstate_flag_p2_tracked | dirstate_flag_merged);
322 } else if (size == dirstate_v1_from_p2) {
316 } else if (size == dirstate_v1_from_p2) {
323 t->flags =
317 t->flags =
324 (dirstate_flag_p2_tracked | dirstate_flag_clean_p2);
318 (dirstate_flag_p2_tracked | dirstate_flag_clean_p2);
325 } else {
319 } else {
326 t->flags = dirstate_flag_p1_tracked;
320 t->flags = dirstate_flag_p1_tracked;
327 }
321 }
328 } else if (state == 'n') {
322 } else if (state == 'n') {
329 if (size == dirstate_v1_from_p2) {
323 if (size == dirstate_v1_from_p2) {
330 t->flags =
324 t->flags =
331 (dirstate_flag_wc_tracked |
325 (dirstate_flag_wc_tracked |
332 dirstate_flag_p2_tracked | dirstate_flag_clean_p2);
326 dirstate_flag_p2_tracked | dirstate_flag_clean_p2);
333 t->mode = 0;
327 t->mode = 0;
334 t->size = dirstate_v1_from_p2;
328 t->size = dirstate_v1_from_p2;
335 t->mtime = ambiguous_time;
329 t->mtime = ambiguous_time;
336 } else if (size == dirstate_v1_nonnormal) {
330 } else if (size == dirstate_v1_nonnormal) {
337 t->flags = (dirstate_flag_wc_tracked |
331 t->flags = (dirstate_flag_wc_tracked |
338 dirstate_flag_p1_tracked |
332 dirstate_flag_p1_tracked |
339 dirstate_flag_possibly_dirty);
333 dirstate_flag_possibly_dirty);
340 t->mode = 0;
334 t->mode = 0;
341 t->size = dirstate_v1_nonnormal;
335 t->size = dirstate_v1_nonnormal;
342 t->mtime = ambiguous_time;
336 t->mtime = ambiguous_time;
343 } else if (mtime == ambiguous_time) {
337 } else if (mtime == ambiguous_time) {
344 t->flags = (dirstate_flag_wc_tracked |
338 t->flags = (dirstate_flag_wc_tracked |
345 dirstate_flag_p1_tracked |
339 dirstate_flag_p1_tracked |
346 dirstate_flag_possibly_dirty);
340 dirstate_flag_possibly_dirty);
347 t->mode = mode;
341 t->mode = mode;
348 t->size = size;
342 t->size = size;
349 t->mtime = 0;
343 t->mtime = 0;
350 } else {
344 } else {
351 t->flags = (dirstate_flag_wc_tracked |
345 t->flags = (dirstate_flag_wc_tracked |
352 dirstate_flag_p1_tracked);
346 dirstate_flag_p1_tracked);
353 t->mode = mode;
347 t->mode = mode;
354 t->size = size;
348 t->size = size;
355 t->mtime = mtime;
349 t->mtime = mtime;
356 }
350 }
357 } else if (state == ' ') {
358 /* XXX Rust is using this special case, it should be clean up
359 * later. */
360 t->flags = dirstate_flag_rust_special;
361 t->mode = mode;
362 t->size = size;
363 t->mtime = mtime;
364 } else {
351 } else {
365 PyErr_Format(PyExc_RuntimeError,
352 PyErr_Format(PyExc_RuntimeError,
366 "unknown state: `%c` (%d, %d, %d)", state, mode,
353 "unknown state: `%c` (%d, %d, %d)", state, mode,
367 size, mtime, NULL);
354 size, mtime, NULL);
368 return NULL;
355 return NULL;
369 }
356 }
370
357
371 return t;
358 return t;
372 }
359 }
373
360
374 /* This will never change since it's bound to V1, unlike `dirstate_item_new` */
361 /* This will never change since it's bound to V1, unlike `dirstate_item_new` */
375 static PyObject *dirstate_item_from_v1_meth(PyTypeObject *subtype,
362 static PyObject *dirstate_item_from_v1_meth(PyTypeObject *subtype,
376 PyObject *args)
363 PyObject *args)
377 {
364 {
378 /* We do all the initialization here and not a tp_init function because
365 /* We do all the initialization here and not a tp_init function because
379 * dirstate_item is immutable. */
366 * dirstate_item is immutable. */
380 char state;
367 char state;
381 int size, mode, mtime;
368 int size, mode, mtime;
382 if (!PyArg_ParseTuple(args, "ciii", &state, &mode, &size, &mtime)) {
369 if (!PyArg_ParseTuple(args, "ciii", &state, &mode, &size, &mtime)) {
383 return NULL;
370 return NULL;
384 }
371 }
385 return (PyObject *)dirstate_item_from_v1_data(state, mode, size, mtime);
372 return (PyObject *)dirstate_item_from_v1_data(state, mode, size, mtime);
386 };
373 };
387
374
388 /* constructor to help legacy API to build a new "added" item
375 /* constructor to help legacy API to build a new "added" item
389
376
390 Should eventually be removed */
377 Should eventually be removed */
391 static PyObject *dirstate_item_new_added(PyTypeObject *subtype)
378 static PyObject *dirstate_item_new_added(PyTypeObject *subtype)
392 {
379 {
393 dirstateItemObject *t;
380 dirstateItemObject *t;
394 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
381 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
395 if (!t) {
382 if (!t) {
396 return NULL;
383 return NULL;
397 }
384 }
398 t->flags = dirstate_flag_wc_tracked;
385 t->flags = dirstate_flag_wc_tracked;
399 t->mode = 0;
386 t->mode = 0;
400 t->size = dirstate_v1_nonnormal;
387 t->size = dirstate_v1_nonnormal;
401 t->mtime = ambiguous_time;
388 t->mtime = ambiguous_time;
402 return (PyObject *)t;
389 return (PyObject *)t;
403 };
390 };
404
391
405 /* constructor to help legacy API to build a new "merged" item
392 /* constructor to help legacy API to build a new "merged" item
406
393
407 Should eventually be removed */
394 Should eventually be removed */
408 static PyObject *dirstate_item_new_merged(PyTypeObject *subtype)
395 static PyObject *dirstate_item_new_merged(PyTypeObject *subtype)
409 {
396 {
410 dirstateItemObject *t;
397 dirstateItemObject *t;
411 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
398 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
412 if (!t) {
399 if (!t) {
413 return NULL;
400 return NULL;
414 }
401 }
415 t->flags = (dirstate_flag_wc_tracked | dirstate_flag_p1_tracked |
402 t->flags = (dirstate_flag_wc_tracked | dirstate_flag_p1_tracked |
416 dirstate_flag_p2_tracked | dirstate_flag_merged);
403 dirstate_flag_p2_tracked | dirstate_flag_merged);
417 t->mode = 0;
404 t->mode = 0;
418 t->size = dirstate_v1_from_p2;
405 t->size = dirstate_v1_from_p2;
419 t->mtime = ambiguous_time;
406 t->mtime = ambiguous_time;
420 return (PyObject *)t;
407 return (PyObject *)t;
421 };
408 };
422
409
423 /* constructor to help legacy API to build a new "from_p2" item
410 /* constructor to help legacy API to build a new "from_p2" item
424
411
425 Should eventually be removed */
412 Should eventually be removed */
426 static PyObject *dirstate_item_new_from_p2(PyTypeObject *subtype)
413 static PyObject *dirstate_item_new_from_p2(PyTypeObject *subtype)
427 {
414 {
428 /* We do all the initialization here and not a tp_init function because
415 /* We do all the initialization here and not a tp_init function because
429 * dirstate_item is immutable. */
416 * dirstate_item is immutable. */
430 dirstateItemObject *t;
417 dirstateItemObject *t;
431 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
418 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
432 if (!t) {
419 if (!t) {
433 return NULL;
420 return NULL;
434 }
421 }
435 t->flags = (dirstate_flag_wc_tracked | dirstate_flag_p2_tracked |
422 t->flags = (dirstate_flag_wc_tracked | dirstate_flag_p2_tracked |
436 dirstate_flag_clean_p2);
423 dirstate_flag_clean_p2);
437 t->mode = 0;
424 t->mode = 0;
438 t->size = dirstate_v1_from_p2;
425 t->size = dirstate_v1_from_p2;
439 t->mtime = ambiguous_time;
426 t->mtime = ambiguous_time;
440 return (PyObject *)t;
427 return (PyObject *)t;
441 };
428 };
442
429
443 /* constructor to help legacy API to build a new "possibly" item
430 /* constructor to help legacy API to build a new "possibly" item
444
431
445 Should eventually be removed */
432 Should eventually be removed */
446 static PyObject *dirstate_item_new_possibly_dirty(PyTypeObject *subtype)
433 static PyObject *dirstate_item_new_possibly_dirty(PyTypeObject *subtype)
447 {
434 {
448 /* We do all the initialization here and not a tp_init function because
435 /* We do all the initialization here and not a tp_init function because
449 * dirstate_item is immutable. */
436 * dirstate_item is immutable. */
450 dirstateItemObject *t;
437 dirstateItemObject *t;
451 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
438 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
452 if (!t) {
439 if (!t) {
453 return NULL;
440 return NULL;
454 }
441 }
455 t->flags = (dirstate_flag_wc_tracked | dirstate_flag_p1_tracked |
442 t->flags = (dirstate_flag_wc_tracked | dirstate_flag_p1_tracked |
456 dirstate_flag_possibly_dirty);
443 dirstate_flag_possibly_dirty);
457 t->mode = 0;
444 t->mode = 0;
458 t->size = dirstate_v1_nonnormal;
445 t->size = dirstate_v1_nonnormal;
459 t->mtime = ambiguous_time;
446 t->mtime = ambiguous_time;
460 return (PyObject *)t;
447 return (PyObject *)t;
461 };
448 };
462
449
463 /* constructor to help legacy API to build a new "normal" item
450 /* constructor to help legacy API to build a new "normal" item
464
451
465 Should eventually be removed */
452 Should eventually be removed */
466 static PyObject *dirstate_item_new_normal(PyTypeObject *subtype, PyObject *args)
453 static PyObject *dirstate_item_new_normal(PyTypeObject *subtype, PyObject *args)
467 {
454 {
468 /* We do all the initialization here and not a tp_init function because
455 /* We do all the initialization here and not a tp_init function because
469 * dirstate_item is immutable. */
456 * dirstate_item is immutable. */
470 dirstateItemObject *t;
457 dirstateItemObject *t;
471 int size, mode, mtime;
458 int size, mode, mtime;
472 if (!PyArg_ParseTuple(args, "iii", &mode, &size, &mtime)) {
459 if (!PyArg_ParseTuple(args, "iii", &mode, &size, &mtime)) {
473 return NULL;
460 return NULL;
474 }
461 }
475
462
476 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
463 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
477 if (!t) {
464 if (!t) {
478 return NULL;
465 return NULL;
479 }
466 }
480 t->flags = (dirstate_flag_wc_tracked | dirstate_flag_p1_tracked);
467 t->flags = (dirstate_flag_wc_tracked | dirstate_flag_p1_tracked);
481 t->mode = mode;
468 t->mode = mode;
482 t->size = size;
469 t->size = size;
483 t->mtime = mtime;
470 t->mtime = mtime;
484 return (PyObject *)t;
471 return (PyObject *)t;
485 };
472 };
486
473
487 /* This means the next status call will have to actually check its content
474 /* This means the next status call will have to actually check its content
488 to make sure it is correct. */
475 to make sure it is correct. */
489 static PyObject *dirstate_item_set_possibly_dirty(dirstateItemObject *self)
476 static PyObject *dirstate_item_set_possibly_dirty(dirstateItemObject *self)
490 {
477 {
491 self->flags |= dirstate_flag_possibly_dirty;
478 self->flags |= dirstate_flag_possibly_dirty;
492 Py_RETURN_NONE;
479 Py_RETURN_NONE;
493 }
480 }
494
481
495 /* See docstring of the python implementation for details */
482 /* See docstring of the python implementation for details */
496 static PyObject *dirstate_item_set_clean(dirstateItemObject *self,
483 static PyObject *dirstate_item_set_clean(dirstateItemObject *self,
497 PyObject *args)
484 PyObject *args)
498 {
485 {
499 int size, mode, mtime;
486 int size, mode, mtime;
500 if (!PyArg_ParseTuple(args, "iii", &mode, &size, &mtime)) {
487 if (!PyArg_ParseTuple(args, "iii", &mode, &size, &mtime)) {
501 return NULL;
488 return NULL;
502 }
489 }
503 self->flags = dirstate_flag_wc_tracked | dirstate_flag_p1_tracked;
490 self->flags = dirstate_flag_wc_tracked | dirstate_flag_p1_tracked;
504 self->mode = mode;
491 self->mode = mode;
505 self->size = size;
492 self->size = size;
506 self->mtime = mtime;
493 self->mtime = mtime;
507 Py_RETURN_NONE;
494 Py_RETURN_NONE;
508 }
495 }
509
496
510 static PyObject *dirstate_item_set_tracked(dirstateItemObject *self)
497 static PyObject *dirstate_item_set_tracked(dirstateItemObject *self)
511 {
498 {
512 self->flags |= dirstate_flag_wc_tracked;
499 self->flags |= dirstate_flag_wc_tracked;
513 self->flags |= dirstate_flag_possibly_dirty;
500 self->flags |= dirstate_flag_possibly_dirty;
514 /* size = None on the python size turn into size = NON_NORMAL when
501 /* size = None on the python size turn into size = NON_NORMAL when
515 * accessed. So the next line is currently required, but a some future
502 * accessed. So the next line is currently required, but a some future
516 * clean up would be welcome. */
503 * clean up would be welcome. */
517 self->size = dirstate_v1_nonnormal;
504 self->size = dirstate_v1_nonnormal;
518 Py_RETURN_NONE;
505 Py_RETURN_NONE;
519 }
506 }
520
507
521 static PyObject *dirstate_item_set_untracked(dirstateItemObject *self)
508 static PyObject *dirstate_item_set_untracked(dirstateItemObject *self)
522 {
509 {
523 self->flags &= ~dirstate_flag_wc_tracked;
510 self->flags &= ~dirstate_flag_wc_tracked;
524 self->mode = 0;
511 self->mode = 0;
525 self->mtime = 0;
512 self->mtime = 0;
526 self->size = 0;
513 self->size = 0;
527 Py_RETURN_NONE;
514 Py_RETURN_NONE;
528 }
515 }
529
516
530 static PyMethodDef dirstate_item_methods[] = {
517 static PyMethodDef dirstate_item_methods[] = {
531 {"v1_state", (PyCFunction)dirstate_item_v1_state, METH_NOARGS,
518 {"v1_state", (PyCFunction)dirstate_item_v1_state, METH_NOARGS,
532 "return a \"state\" suitable for v1 serialization"},
519 "return a \"state\" suitable for v1 serialization"},
533 {"v1_mode", (PyCFunction)dirstate_item_v1_mode, METH_NOARGS,
520 {"v1_mode", (PyCFunction)dirstate_item_v1_mode, METH_NOARGS,
534 "return a \"mode\" suitable for v1 serialization"},
521 "return a \"mode\" suitable for v1 serialization"},
535 {"v1_size", (PyCFunction)dirstate_item_v1_size, METH_NOARGS,
522 {"v1_size", (PyCFunction)dirstate_item_v1_size, METH_NOARGS,
536 "return a \"size\" suitable for v1 serialization"},
523 "return a \"size\" suitable for v1 serialization"},
537 {"v1_mtime", (PyCFunction)dirstate_item_v1_mtime, METH_NOARGS,
524 {"v1_mtime", (PyCFunction)dirstate_item_v1_mtime, METH_NOARGS,
538 "return a \"mtime\" suitable for v1 serialization"},
525 "return a \"mtime\" suitable for v1 serialization"},
539 {"need_delay", (PyCFunction)dirstate_item_need_delay, METH_O,
526 {"need_delay", (PyCFunction)dirstate_item_need_delay, METH_O,
540 "True if the stored mtime would be ambiguous with the current time"},
527 "True if the stored mtime would be ambiguous with the current time"},
541 {"from_v1_data", (PyCFunction)dirstate_item_from_v1_meth,
528 {"from_v1_data", (PyCFunction)dirstate_item_from_v1_meth,
542 METH_VARARGS | METH_CLASS, "build a new DirstateItem object from V1 data"},
529 METH_VARARGS | METH_CLASS, "build a new DirstateItem object from V1 data"},
543 {"new_added", (PyCFunction)dirstate_item_new_added,
530 {"new_added", (PyCFunction)dirstate_item_new_added,
544 METH_NOARGS | METH_CLASS,
531 METH_NOARGS | METH_CLASS,
545 "constructor to help legacy API to build a new \"added\" item"},
532 "constructor to help legacy API to build a new \"added\" item"},
546 {"new_merged", (PyCFunction)dirstate_item_new_merged,
533 {"new_merged", (PyCFunction)dirstate_item_new_merged,
547 METH_NOARGS | METH_CLASS,
534 METH_NOARGS | METH_CLASS,
548 "constructor to help legacy API to build a new \"merged\" item"},
535 "constructor to help legacy API to build a new \"merged\" item"},
549 {"new_from_p2", (PyCFunction)dirstate_item_new_from_p2,
536 {"new_from_p2", (PyCFunction)dirstate_item_new_from_p2,
550 METH_NOARGS | METH_CLASS,
537 METH_NOARGS | METH_CLASS,
551 "constructor to help legacy API to build a new \"from_p2\" item"},
538 "constructor to help legacy API to build a new \"from_p2\" item"},
552 {"new_possibly_dirty", (PyCFunction)dirstate_item_new_possibly_dirty,
539 {"new_possibly_dirty", (PyCFunction)dirstate_item_new_possibly_dirty,
553 METH_NOARGS | METH_CLASS,
540 METH_NOARGS | METH_CLASS,
554 "constructor to help legacy API to build a new \"possibly_dirty\" item"},
541 "constructor to help legacy API to build a new \"possibly_dirty\" item"},
555 {"new_normal", (PyCFunction)dirstate_item_new_normal,
542 {"new_normal", (PyCFunction)dirstate_item_new_normal,
556 METH_VARARGS | METH_CLASS,
543 METH_VARARGS | METH_CLASS,
557 "constructor to help legacy API to build a new \"normal\" item"},
544 "constructor to help legacy API to build a new \"normal\" item"},
558 {"set_possibly_dirty", (PyCFunction)dirstate_item_set_possibly_dirty,
545 {"set_possibly_dirty", (PyCFunction)dirstate_item_set_possibly_dirty,
559 METH_NOARGS, "mark a file as \"possibly dirty\""},
546 METH_NOARGS, "mark a file as \"possibly dirty\""},
560 {"set_clean", (PyCFunction)dirstate_item_set_clean, METH_VARARGS,
547 {"set_clean", (PyCFunction)dirstate_item_set_clean, METH_VARARGS,
561 "mark a file as \"clean\""},
548 "mark a file as \"clean\""},
562 {"set_tracked", (PyCFunction)dirstate_item_set_tracked, METH_NOARGS,
549 {"set_tracked", (PyCFunction)dirstate_item_set_tracked, METH_NOARGS,
563 "mark a file as \"tracked\""},
550 "mark a file as \"tracked\""},
564 {"set_untracked", (PyCFunction)dirstate_item_set_untracked, METH_NOARGS,
551 {"set_untracked", (PyCFunction)dirstate_item_set_untracked, METH_NOARGS,
565 "mark a file as \"untracked\""},
552 "mark a file as \"untracked\""},
566 {NULL} /* Sentinel */
553 {NULL} /* Sentinel */
567 };
554 };
568
555
569 static PyObject *dirstate_item_get_mode(dirstateItemObject *self)
556 static PyObject *dirstate_item_get_mode(dirstateItemObject *self)
570 {
557 {
571 return PyInt_FromLong(dirstate_item_c_v1_mode(self));
558 return PyInt_FromLong(dirstate_item_c_v1_mode(self));
572 };
559 };
573
560
574 static PyObject *dirstate_item_get_size(dirstateItemObject *self)
561 static PyObject *dirstate_item_get_size(dirstateItemObject *self)
575 {
562 {
576 return PyInt_FromLong(dirstate_item_c_v1_size(self));
563 return PyInt_FromLong(dirstate_item_c_v1_size(self));
577 };
564 };
578
565
579 static PyObject *dirstate_item_get_mtime(dirstateItemObject *self)
566 static PyObject *dirstate_item_get_mtime(dirstateItemObject *self)
580 {
567 {
581 return PyInt_FromLong(dirstate_item_c_v1_mtime(self));
568 return PyInt_FromLong(dirstate_item_c_v1_mtime(self));
582 };
569 };
583
570
584 static PyObject *dirstate_item_get_state(dirstateItemObject *self)
571 static PyObject *dirstate_item_get_state(dirstateItemObject *self)
585 {
572 {
586 char state = dirstate_item_c_v1_state(self);
573 char state = dirstate_item_c_v1_state(self);
587 return PyBytes_FromStringAndSize(&state, 1);
574 return PyBytes_FromStringAndSize(&state, 1);
588 };
575 };
589
576
590 static PyObject *dirstate_item_get_tracked(dirstateItemObject *self)
577 static PyObject *dirstate_item_get_tracked(dirstateItemObject *self)
591 {
578 {
592 if (dirstate_item_c_tracked(self)) {
579 if (dirstate_item_c_tracked(self)) {
593 Py_RETURN_TRUE;
580 Py_RETURN_TRUE;
594 } else {
581 } else {
595 Py_RETURN_FALSE;
582 Py_RETURN_FALSE;
596 }
583 }
597 };
584 };
598
585
599 static PyObject *dirstate_item_get_added(dirstateItemObject *self)
586 static PyObject *dirstate_item_get_added(dirstateItemObject *self)
600 {
587 {
601 if (dirstate_item_c_added(self)) {
588 if (dirstate_item_c_added(self)) {
602 Py_RETURN_TRUE;
589 Py_RETURN_TRUE;
603 } else {
590 } else {
604 Py_RETURN_FALSE;
591 Py_RETURN_FALSE;
605 }
592 }
606 };
593 };
607
594
608 static PyObject *dirstate_item_get_merged(dirstateItemObject *self)
595 static PyObject *dirstate_item_get_merged(dirstateItemObject *self)
609 {
596 {
610 if (dirstate_item_c_merged(self)) {
597 if (dirstate_item_c_merged(self)) {
611 Py_RETURN_TRUE;
598 Py_RETURN_TRUE;
612 } else {
599 } else {
613 Py_RETURN_FALSE;
600 Py_RETURN_FALSE;
614 }
601 }
615 };
602 };
616
603
617 static PyObject *dirstate_item_get_merged_removed(dirstateItemObject *self)
604 static PyObject *dirstate_item_get_merged_removed(dirstateItemObject *self)
618 {
605 {
619 if (dirstate_item_c_merged_removed(self)) {
606 if (dirstate_item_c_merged_removed(self)) {
620 Py_RETURN_TRUE;
607 Py_RETURN_TRUE;
621 } else {
608 } else {
622 Py_RETURN_FALSE;
609 Py_RETURN_FALSE;
623 }
610 }
624 };
611 };
625
612
626 static PyObject *dirstate_item_get_from_p2(dirstateItemObject *self)
613 static PyObject *dirstate_item_get_from_p2(dirstateItemObject *self)
627 {
614 {
628 if (dirstate_item_c_from_p2(self)) {
615 if (dirstate_item_c_from_p2(self)) {
629 Py_RETURN_TRUE;
616 Py_RETURN_TRUE;
630 } else {
617 } else {
631 Py_RETURN_FALSE;
618 Py_RETURN_FALSE;
632 }
619 }
633 };
620 };
634
621
635 static PyObject *dirstate_item_get_from_p2_removed(dirstateItemObject *self)
622 static PyObject *dirstate_item_get_from_p2_removed(dirstateItemObject *self)
636 {
623 {
637 if (dirstate_item_c_from_p2_removed(self)) {
624 if (dirstate_item_c_from_p2_removed(self)) {
638 Py_RETURN_TRUE;
625 Py_RETURN_TRUE;
639 } else {
626 } else {
640 Py_RETURN_FALSE;
627 Py_RETURN_FALSE;
641 }
628 }
642 };
629 };
643
630
644 static PyObject *dirstate_item_get_removed(dirstateItemObject *self)
631 static PyObject *dirstate_item_get_removed(dirstateItemObject *self)
645 {
632 {
646 if (dirstate_item_c_removed(self)) {
633 if (dirstate_item_c_removed(self)) {
647 Py_RETURN_TRUE;
634 Py_RETURN_TRUE;
648 } else {
635 } else {
649 Py_RETURN_FALSE;
636 Py_RETURN_FALSE;
650 }
637 }
651 };
638 };
652
639
653 static PyObject *dm_nonnormal(dirstateItemObject *self)
640 static PyObject *dm_nonnormal(dirstateItemObject *self)
654 {
641 {
655 if ((dirstate_item_c_v1_state(self) != 'n') ||
642 if ((dirstate_item_c_v1_state(self) != 'n') ||
656 (dirstate_item_c_v1_mtime(self) == ambiguous_time)) {
643 (dirstate_item_c_v1_mtime(self) == ambiguous_time)) {
657 Py_RETURN_TRUE;
644 Py_RETURN_TRUE;
658 } else {
645 } else {
659 Py_RETURN_FALSE;
646 Py_RETURN_FALSE;
660 }
647 }
661 };
648 };
662 static PyObject *dm_otherparent(dirstateItemObject *self)
649 static PyObject *dm_otherparent(dirstateItemObject *self)
663 {
650 {
664 if (dirstate_item_c_v1_mtime(self) == dirstate_v1_from_p2) {
651 if (dirstate_item_c_v1_mtime(self) == dirstate_v1_from_p2) {
665 Py_RETURN_TRUE;
652 Py_RETURN_TRUE;
666 } else {
653 } else {
667 Py_RETURN_FALSE;
654 Py_RETURN_FALSE;
668 }
655 }
669 };
656 };
670
657
671 static PyGetSetDef dirstate_item_getset[] = {
658 static PyGetSetDef dirstate_item_getset[] = {
672 {"mode", (getter)dirstate_item_get_mode, NULL, "mode", NULL},
659 {"mode", (getter)dirstate_item_get_mode, NULL, "mode", NULL},
673 {"size", (getter)dirstate_item_get_size, NULL, "size", NULL},
660 {"size", (getter)dirstate_item_get_size, NULL, "size", NULL},
674 {"mtime", (getter)dirstate_item_get_mtime, NULL, "mtime", NULL},
661 {"mtime", (getter)dirstate_item_get_mtime, NULL, "mtime", NULL},
675 {"state", (getter)dirstate_item_get_state, NULL, "state", NULL},
662 {"state", (getter)dirstate_item_get_state, NULL, "state", NULL},
676 {"tracked", (getter)dirstate_item_get_tracked, NULL, "tracked", NULL},
663 {"tracked", (getter)dirstate_item_get_tracked, NULL, "tracked", NULL},
677 {"added", (getter)dirstate_item_get_added, NULL, "added", NULL},
664 {"added", (getter)dirstate_item_get_added, NULL, "added", NULL},
678 {"merged_removed", (getter)dirstate_item_get_merged_removed, NULL,
665 {"merged_removed", (getter)dirstate_item_get_merged_removed, NULL,
679 "merged_removed", NULL},
666 "merged_removed", NULL},
680 {"merged", (getter)dirstate_item_get_merged, NULL, "merged", NULL},
667 {"merged", (getter)dirstate_item_get_merged, NULL, "merged", NULL},
681 {"from_p2_removed", (getter)dirstate_item_get_from_p2_removed, NULL,
668 {"from_p2_removed", (getter)dirstate_item_get_from_p2_removed, NULL,
682 "from_p2_removed", NULL},
669 "from_p2_removed", NULL},
683 {"from_p2", (getter)dirstate_item_get_from_p2, NULL, "from_p2", NULL},
670 {"from_p2", (getter)dirstate_item_get_from_p2, NULL, "from_p2", NULL},
684 {"removed", (getter)dirstate_item_get_removed, NULL, "removed", NULL},
671 {"removed", (getter)dirstate_item_get_removed, NULL, "removed", NULL},
685 {"dm_nonnormal", (getter)dm_nonnormal, NULL, "dm_nonnormal", NULL},
672 {"dm_nonnormal", (getter)dm_nonnormal, NULL, "dm_nonnormal", NULL},
686 {"dm_otherparent", (getter)dm_otherparent, NULL, "dm_otherparent", NULL},
673 {"dm_otherparent", (getter)dm_otherparent, NULL, "dm_otherparent", NULL},
687 {NULL} /* Sentinel */
674 {NULL} /* Sentinel */
688 };
675 };
689
676
690 PyTypeObject dirstateItemType = {
677 PyTypeObject dirstateItemType = {
691 PyVarObject_HEAD_INIT(NULL, 0) /* header */
678 PyVarObject_HEAD_INIT(NULL, 0) /* header */
692 "dirstate_tuple", /* tp_name */
679 "dirstate_tuple", /* tp_name */
693 sizeof(dirstateItemObject), /* tp_basicsize */
680 sizeof(dirstateItemObject), /* tp_basicsize */
694 0, /* tp_itemsize */
681 0, /* tp_itemsize */
695 (destructor)dirstate_item_dealloc, /* tp_dealloc */
682 (destructor)dirstate_item_dealloc, /* tp_dealloc */
696 0, /* tp_print */
683 0, /* tp_print */
697 0, /* tp_getattr */
684 0, /* tp_getattr */
698 0, /* tp_setattr */
685 0, /* tp_setattr */
699 0, /* tp_compare */
686 0, /* tp_compare */
700 0, /* tp_repr */
687 0, /* tp_repr */
701 0, /* tp_as_number */
688 0, /* tp_as_number */
702 0, /* tp_as_sequence */
689 0, /* tp_as_sequence */
703 0, /* tp_as_mapping */
690 0, /* tp_as_mapping */
704 0, /* tp_hash */
691 0, /* tp_hash */
705 0, /* tp_call */
692 0, /* tp_call */
706 0, /* tp_str */
693 0, /* tp_str */
707 0, /* tp_getattro */
694 0, /* tp_getattro */
708 0, /* tp_setattro */
695 0, /* tp_setattro */
709 0, /* tp_as_buffer */
696 0, /* tp_as_buffer */
710 Py_TPFLAGS_DEFAULT, /* tp_flags */
697 Py_TPFLAGS_DEFAULT, /* tp_flags */
711 "dirstate tuple", /* tp_doc */
698 "dirstate tuple", /* tp_doc */
712 0, /* tp_traverse */
699 0, /* tp_traverse */
713 0, /* tp_clear */
700 0, /* tp_clear */
714 0, /* tp_richcompare */
701 0, /* tp_richcompare */
715 0, /* tp_weaklistoffset */
702 0, /* tp_weaklistoffset */
716 0, /* tp_iter */
703 0, /* tp_iter */
717 0, /* tp_iternext */
704 0, /* tp_iternext */
718 dirstate_item_methods, /* tp_methods */
705 dirstate_item_methods, /* tp_methods */
719 0, /* tp_members */
706 0, /* tp_members */
720 dirstate_item_getset, /* tp_getset */
707 dirstate_item_getset, /* tp_getset */
721 0, /* tp_base */
708 0, /* tp_base */
722 0, /* tp_dict */
709 0, /* tp_dict */
723 0, /* tp_descr_get */
710 0, /* tp_descr_get */
724 0, /* tp_descr_set */
711 0, /* tp_descr_set */
725 0, /* tp_dictoffset */
712 0, /* tp_dictoffset */
726 0, /* tp_init */
713 0, /* tp_init */
727 0, /* tp_alloc */
714 0, /* tp_alloc */
728 dirstate_item_new, /* tp_new */
715 dirstate_item_new, /* tp_new */
729 };
716 };
730
717
731 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
718 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
732 {
719 {
733 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
720 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
734 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
721 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
735 char state, *cur, *str, *cpos;
722 char state, *cur, *str, *cpos;
736 int mode, size, mtime;
723 int mode, size, mtime;
737 unsigned int flen, pos = 40;
724 unsigned int flen, pos = 40;
738 Py_ssize_t len = 40;
725 Py_ssize_t len = 40;
739 Py_ssize_t readlen;
726 Py_ssize_t readlen;
740
727
741 if (!PyArg_ParseTuple(
728 if (!PyArg_ParseTuple(
742 args, PY23("O!O!s#:parse_dirstate", "O!O!y#:parse_dirstate"),
729 args, PY23("O!O!s#:parse_dirstate", "O!O!y#:parse_dirstate"),
743 &PyDict_Type, &dmap, &PyDict_Type, &cmap, &str, &readlen)) {
730 &PyDict_Type, &dmap, &PyDict_Type, &cmap, &str, &readlen)) {
744 goto quit;
731 goto quit;
745 }
732 }
746
733
747 len = readlen;
734 len = readlen;
748
735
749 /* read parents */
736 /* read parents */
750 if (len < 40) {
737 if (len < 40) {
751 PyErr_SetString(PyExc_ValueError,
738 PyErr_SetString(PyExc_ValueError,
752 "too little data for parents");
739 "too little data for parents");
753 goto quit;
740 goto quit;
754 }
741 }
755
742
756 parents = Py_BuildValue(PY23("s#s#", "y#y#"), str, (Py_ssize_t)20,
743 parents = Py_BuildValue(PY23("s#s#", "y#y#"), str, (Py_ssize_t)20,
757 str + 20, (Py_ssize_t)20);
744 str + 20, (Py_ssize_t)20);
758 if (!parents) {
745 if (!parents) {
759 goto quit;
746 goto quit;
760 }
747 }
761
748
762 /* read filenames */
749 /* read filenames */
763 while (pos >= 40 && pos < len) {
750 while (pos >= 40 && pos < len) {
764 if (pos + 17 > len) {
751 if (pos + 17 > len) {
765 PyErr_SetString(PyExc_ValueError,
752 PyErr_SetString(PyExc_ValueError,
766 "overflow in dirstate");
753 "overflow in dirstate");
767 goto quit;
754 goto quit;
768 }
755 }
769 cur = str + pos;
756 cur = str + pos;
770 /* unpack header */
757 /* unpack header */
771 state = *cur;
758 state = *cur;
772 mode = getbe32(cur + 1);
759 mode = getbe32(cur + 1);
773 size = getbe32(cur + 5);
760 size = getbe32(cur + 5);
774 mtime = getbe32(cur + 9);
761 mtime = getbe32(cur + 9);
775 flen = getbe32(cur + 13);
762 flen = getbe32(cur + 13);
776 pos += 17;
763 pos += 17;
777 cur += 17;
764 cur += 17;
778 if (flen > len - pos) {
765 if (flen > len - pos) {
779 PyErr_SetString(PyExc_ValueError,
766 PyErr_SetString(PyExc_ValueError,
780 "overflow in dirstate");
767 "overflow in dirstate");
781 goto quit;
768 goto quit;
782 }
769 }
783
770
784 entry = (PyObject *)dirstate_item_from_v1_data(state, mode,
771 entry = (PyObject *)dirstate_item_from_v1_data(state, mode,
785 size, mtime);
772 size, mtime);
786 cpos = memchr(cur, 0, flen);
773 cpos = memchr(cur, 0, flen);
787 if (cpos) {
774 if (cpos) {
788 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
775 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
789 cname = PyBytes_FromStringAndSize(
776 cname = PyBytes_FromStringAndSize(
790 cpos + 1, flen - (cpos - cur) - 1);
777 cpos + 1, flen - (cpos - cur) - 1);
791 if (!fname || !cname ||
778 if (!fname || !cname ||
792 PyDict_SetItem(cmap, fname, cname) == -1 ||
779 PyDict_SetItem(cmap, fname, cname) == -1 ||
793 PyDict_SetItem(dmap, fname, entry) == -1) {
780 PyDict_SetItem(dmap, fname, entry) == -1) {
794 goto quit;
781 goto quit;
795 }
782 }
796 Py_DECREF(cname);
783 Py_DECREF(cname);
797 } else {
784 } else {
798 fname = PyBytes_FromStringAndSize(cur, flen);
785 fname = PyBytes_FromStringAndSize(cur, flen);
799 if (!fname ||
786 if (!fname ||
800 PyDict_SetItem(dmap, fname, entry) == -1) {
787 PyDict_SetItem(dmap, fname, entry) == -1) {
801 goto quit;
788 goto quit;
802 }
789 }
803 }
790 }
804 Py_DECREF(fname);
791 Py_DECREF(fname);
805 Py_DECREF(entry);
792 Py_DECREF(entry);
806 fname = cname = entry = NULL;
793 fname = cname = entry = NULL;
807 pos += flen;
794 pos += flen;
808 }
795 }
809
796
810 ret = parents;
797 ret = parents;
811 Py_INCREF(ret);
798 Py_INCREF(ret);
812 quit:
799 quit:
813 Py_XDECREF(fname);
800 Py_XDECREF(fname);
814 Py_XDECREF(cname);
801 Py_XDECREF(cname);
815 Py_XDECREF(entry);
802 Py_XDECREF(entry);
816 Py_XDECREF(parents);
803 Py_XDECREF(parents);
817 return ret;
804 return ret;
818 }
805 }
819
806
820 /*
807 /*
821 * Build a set of non-normal and other parent entries from the dirstate dmap
808 * Build a set of non-normal and other parent entries from the dirstate dmap
822 */
809 */
823 static PyObject *nonnormalotherparententries(PyObject *self, PyObject *args)
810 static PyObject *nonnormalotherparententries(PyObject *self, PyObject *args)
824 {
811 {
825 PyObject *dmap, *fname, *v;
812 PyObject *dmap, *fname, *v;
826 PyObject *nonnset = NULL, *otherpset = NULL, *result = NULL;
813 PyObject *nonnset = NULL, *otherpset = NULL, *result = NULL;
827 Py_ssize_t pos;
814 Py_ssize_t pos;
828
815
829 if (!PyArg_ParseTuple(args, "O!:nonnormalentries", &PyDict_Type,
816 if (!PyArg_ParseTuple(args, "O!:nonnormalentries", &PyDict_Type,
830 &dmap)) {
817 &dmap)) {
831 goto bail;
818 goto bail;
832 }
819 }
833
820
834 nonnset = PySet_New(NULL);
821 nonnset = PySet_New(NULL);
835 if (nonnset == NULL) {
822 if (nonnset == NULL) {
836 goto bail;
823 goto bail;
837 }
824 }
838
825
839 otherpset = PySet_New(NULL);
826 otherpset = PySet_New(NULL);
840 if (otherpset == NULL) {
827 if (otherpset == NULL) {
841 goto bail;
828 goto bail;
842 }
829 }
843
830
844 pos = 0;
831 pos = 0;
845 while (PyDict_Next(dmap, &pos, &fname, &v)) {
832 while (PyDict_Next(dmap, &pos, &fname, &v)) {
846 dirstateItemObject *t;
833 dirstateItemObject *t;
847 if (!dirstate_tuple_check(v)) {
834 if (!dirstate_tuple_check(v)) {
848 PyErr_SetString(PyExc_TypeError,
835 PyErr_SetString(PyExc_TypeError,
849 "expected a dirstate tuple");
836 "expected a dirstate tuple");
850 goto bail;
837 goto bail;
851 }
838 }
852 t = (dirstateItemObject *)v;
839 t = (dirstateItemObject *)v;
853
840
854 if (dirstate_item_c_from_p2(t)) {
841 if (dirstate_item_c_from_p2(t)) {
855 if (PySet_Add(otherpset, fname) == -1) {
842 if (PySet_Add(otherpset, fname) == -1) {
856 goto bail;
843 goto bail;
857 }
844 }
858 }
845 }
859 if (!(t->flags & dirstate_flag_wc_tracked) ||
846 if (!(t->flags & dirstate_flag_wc_tracked) ||
860 !(t->flags &
847 !(t->flags &
861 (dirstate_flag_p1_tracked | dirstate_flag_p2_tracked)) ||
848 (dirstate_flag_p1_tracked | dirstate_flag_p2_tracked)) ||
862 (t->flags &
849 (t->flags &
863 (dirstate_flag_possibly_dirty | dirstate_flag_merged))) {
850 (dirstate_flag_possibly_dirty | dirstate_flag_merged))) {
864 if (PySet_Add(nonnset, fname) == -1) {
851 if (PySet_Add(nonnset, fname) == -1) {
865 goto bail;
852 goto bail;
866 }
853 }
867 }
854 }
868 }
855 }
869
856
870 result = Py_BuildValue("(OO)", nonnset, otherpset);
857 result = Py_BuildValue("(OO)", nonnset, otherpset);
871 if (result == NULL) {
858 if (result == NULL) {
872 goto bail;
859 goto bail;
873 }
860 }
874 Py_DECREF(nonnset);
861 Py_DECREF(nonnset);
875 Py_DECREF(otherpset);
862 Py_DECREF(otherpset);
876 return result;
863 return result;
877 bail:
864 bail:
878 Py_XDECREF(nonnset);
865 Py_XDECREF(nonnset);
879 Py_XDECREF(otherpset);
866 Py_XDECREF(otherpset);
880 Py_XDECREF(result);
867 Py_XDECREF(result);
881 return NULL;
868 return NULL;
882 }
869 }
883
870
884 /*
871 /*
885 * Efficiently pack a dirstate object into its on-disk format.
872 * Efficiently pack a dirstate object into its on-disk format.
886 */
873 */
887 static PyObject *pack_dirstate(PyObject *self, PyObject *args)
874 static PyObject *pack_dirstate(PyObject *self, PyObject *args)
888 {
875 {
889 PyObject *packobj = NULL;
876 PyObject *packobj = NULL;
890 PyObject *map, *copymap, *pl, *mtime_unset = NULL;
877 PyObject *map, *copymap, *pl, *mtime_unset = NULL;
891 Py_ssize_t nbytes, pos, l;
878 Py_ssize_t nbytes, pos, l;
892 PyObject *k, *v = NULL, *pn;
879 PyObject *k, *v = NULL, *pn;
893 char *p, *s;
880 char *p, *s;
894 int now;
881 int now;
895
882
896 if (!PyArg_ParseTuple(args, "O!O!O!i:pack_dirstate", &PyDict_Type, &map,
883 if (!PyArg_ParseTuple(args, "O!O!O!i:pack_dirstate", &PyDict_Type, &map,
897 &PyDict_Type, &copymap, &PyTuple_Type, &pl,
884 &PyDict_Type, &copymap, &PyTuple_Type, &pl,
898 &now)) {
885 &now)) {
899 return NULL;
886 return NULL;
900 }
887 }
901
888
902 if (PyTuple_Size(pl) != 2) {
889 if (PyTuple_Size(pl) != 2) {
903 PyErr_SetString(PyExc_TypeError, "expected 2-element tuple");
890 PyErr_SetString(PyExc_TypeError, "expected 2-element tuple");
904 return NULL;
891 return NULL;
905 }
892 }
906
893
907 /* Figure out how much we need to allocate. */
894 /* Figure out how much we need to allocate. */
908 for (nbytes = 40, pos = 0; PyDict_Next(map, &pos, &k, &v);) {
895 for (nbytes = 40, pos = 0; PyDict_Next(map, &pos, &k, &v);) {
909 PyObject *c;
896 PyObject *c;
910 if (!PyBytes_Check(k)) {
897 if (!PyBytes_Check(k)) {
911 PyErr_SetString(PyExc_TypeError, "expected string key");
898 PyErr_SetString(PyExc_TypeError, "expected string key");
912 goto bail;
899 goto bail;
913 }
900 }
914 nbytes += PyBytes_GET_SIZE(k) + 17;
901 nbytes += PyBytes_GET_SIZE(k) + 17;
915 c = PyDict_GetItem(copymap, k);
902 c = PyDict_GetItem(copymap, k);
916 if (c) {
903 if (c) {
917 if (!PyBytes_Check(c)) {
904 if (!PyBytes_Check(c)) {
918 PyErr_SetString(PyExc_TypeError,
905 PyErr_SetString(PyExc_TypeError,
919 "expected string key");
906 "expected string key");
920 goto bail;
907 goto bail;
921 }
908 }
922 nbytes += PyBytes_GET_SIZE(c) + 1;
909 nbytes += PyBytes_GET_SIZE(c) + 1;
923 }
910 }
924 }
911 }
925
912
926 packobj = PyBytes_FromStringAndSize(NULL, nbytes);
913 packobj = PyBytes_FromStringAndSize(NULL, nbytes);
927 if (packobj == NULL) {
914 if (packobj == NULL) {
928 goto bail;
915 goto bail;
929 }
916 }
930
917
931 p = PyBytes_AS_STRING(packobj);
918 p = PyBytes_AS_STRING(packobj);
932
919
933 pn = PyTuple_GET_ITEM(pl, 0);
920 pn = PyTuple_GET_ITEM(pl, 0);
934 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
921 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
935 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
922 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
936 goto bail;
923 goto bail;
937 }
924 }
938 memcpy(p, s, l);
925 memcpy(p, s, l);
939 p += 20;
926 p += 20;
940 pn = PyTuple_GET_ITEM(pl, 1);
927 pn = PyTuple_GET_ITEM(pl, 1);
941 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
928 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
942 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
929 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
943 goto bail;
930 goto bail;
944 }
931 }
945 memcpy(p, s, l);
932 memcpy(p, s, l);
946 p += 20;
933 p += 20;
947
934
948 for (pos = 0; PyDict_Next(map, &pos, &k, &v);) {
935 for (pos = 0; PyDict_Next(map, &pos, &k, &v);) {
949 dirstateItemObject *tuple;
936 dirstateItemObject *tuple;
950 char state;
937 char state;
951 int mode, size, mtime;
938 int mode, size, mtime;
952 Py_ssize_t len, l;
939 Py_ssize_t len, l;
953 PyObject *o;
940 PyObject *o;
954 char *t;
941 char *t;
955
942
956 if (!dirstate_tuple_check(v)) {
943 if (!dirstate_tuple_check(v)) {
957 PyErr_SetString(PyExc_TypeError,
944 PyErr_SetString(PyExc_TypeError,
958 "expected a dirstate tuple");
945 "expected a dirstate tuple");
959 goto bail;
946 goto bail;
960 }
947 }
961 tuple = (dirstateItemObject *)v;
948 tuple = (dirstateItemObject *)v;
962
949
963 state = dirstate_item_c_v1_state(tuple);
950 state = dirstate_item_c_v1_state(tuple);
964 mode = dirstate_item_c_v1_mode(tuple);
951 mode = dirstate_item_c_v1_mode(tuple);
965 size = dirstate_item_c_v1_size(tuple);
952 size = dirstate_item_c_v1_size(tuple);
966 mtime = dirstate_item_c_v1_mtime(tuple);
953 mtime = dirstate_item_c_v1_mtime(tuple);
967 if (state == 'n' && mtime == now) {
954 if (state == 'n' && mtime == now) {
968 /* See pure/parsers.py:pack_dirstate for why we do
955 /* See pure/parsers.py:pack_dirstate for why we do
969 * this. */
956 * this. */
970 mtime = -1;
957 mtime = -1;
971 mtime_unset = (PyObject *)dirstate_item_from_v1_data(
958 mtime_unset = (PyObject *)dirstate_item_from_v1_data(
972 state, mode, size, mtime);
959 state, mode, size, mtime);
973 if (!mtime_unset) {
960 if (!mtime_unset) {
974 goto bail;
961 goto bail;
975 }
962 }
976 if (PyDict_SetItem(map, k, mtime_unset) == -1) {
963 if (PyDict_SetItem(map, k, mtime_unset) == -1) {
977 goto bail;
964 goto bail;
978 }
965 }
979 Py_DECREF(mtime_unset);
966 Py_DECREF(mtime_unset);
980 mtime_unset = NULL;
967 mtime_unset = NULL;
981 }
968 }
982 *p++ = state;
969 *p++ = state;
983 putbe32((uint32_t)mode, p);
970 putbe32((uint32_t)mode, p);
984 putbe32((uint32_t)size, p + 4);
971 putbe32((uint32_t)size, p + 4);
985 putbe32((uint32_t)mtime, p + 8);
972 putbe32((uint32_t)mtime, p + 8);
986 t = p + 12;
973 t = p + 12;
987 p += 16;
974 p += 16;
988 len = PyBytes_GET_SIZE(k);
975 len = PyBytes_GET_SIZE(k);
989 memcpy(p, PyBytes_AS_STRING(k), len);
976 memcpy(p, PyBytes_AS_STRING(k), len);
990 p += len;
977 p += len;
991 o = PyDict_GetItem(copymap, k);
978 o = PyDict_GetItem(copymap, k);
992 if (o) {
979 if (o) {
993 *p++ = '\0';
980 *p++ = '\0';
994 l = PyBytes_GET_SIZE(o);
981 l = PyBytes_GET_SIZE(o);
995 memcpy(p, PyBytes_AS_STRING(o), l);
982 memcpy(p, PyBytes_AS_STRING(o), l);
996 p += l;
983 p += l;
997 len += l + 1;
984 len += l + 1;
998 }
985 }
999 putbe32((uint32_t)len, t);
986 putbe32((uint32_t)len, t);
1000 }
987 }
1001
988
1002 pos = p - PyBytes_AS_STRING(packobj);
989 pos = p - PyBytes_AS_STRING(packobj);
1003 if (pos != nbytes) {
990 if (pos != nbytes) {
1004 PyErr_Format(PyExc_SystemError, "bad dirstate size: %ld != %ld",
991 PyErr_Format(PyExc_SystemError, "bad dirstate size: %ld != %ld",
1005 (long)pos, (long)nbytes);
992 (long)pos, (long)nbytes);
1006 goto bail;
993 goto bail;
1007 }
994 }
1008
995
1009 return packobj;
996 return packobj;
1010 bail:
997 bail:
1011 Py_XDECREF(mtime_unset);
998 Py_XDECREF(mtime_unset);
1012 Py_XDECREF(packobj);
999 Py_XDECREF(packobj);
1013 Py_XDECREF(v);
1000 Py_XDECREF(v);
1014 return NULL;
1001 return NULL;
1015 }
1002 }
1016
1003
1017 #define BUMPED_FIX 1
1004 #define BUMPED_FIX 1
1018 #define USING_SHA_256 2
1005 #define USING_SHA_256 2
1019 #define FM1_HEADER_SIZE (4 + 8 + 2 + 2 + 1 + 1 + 1)
1006 #define FM1_HEADER_SIZE (4 + 8 + 2 + 2 + 1 + 1 + 1)
1020
1007
1021 static PyObject *readshas(const char *source, unsigned char num,
1008 static PyObject *readshas(const char *source, unsigned char num,
1022 Py_ssize_t hashwidth)
1009 Py_ssize_t hashwidth)
1023 {
1010 {
1024 int i;
1011 int i;
1025 PyObject *list = PyTuple_New(num);
1012 PyObject *list = PyTuple_New(num);
1026 if (list == NULL) {
1013 if (list == NULL) {
1027 return NULL;
1014 return NULL;
1028 }
1015 }
1029 for (i = 0; i < num; i++) {
1016 for (i = 0; i < num; i++) {
1030 PyObject *hash = PyBytes_FromStringAndSize(source, hashwidth);
1017 PyObject *hash = PyBytes_FromStringAndSize(source, hashwidth);
1031 if (hash == NULL) {
1018 if (hash == NULL) {
1032 Py_DECREF(list);
1019 Py_DECREF(list);
1033 return NULL;
1020 return NULL;
1034 }
1021 }
1035 PyTuple_SET_ITEM(list, i, hash);
1022 PyTuple_SET_ITEM(list, i, hash);
1036 source += hashwidth;
1023 source += hashwidth;
1037 }
1024 }
1038 return list;
1025 return list;
1039 }
1026 }
1040
1027
1041 static PyObject *fm1readmarker(const char *databegin, const char *dataend,
1028 static PyObject *fm1readmarker(const char *databegin, const char *dataend,
1042 uint32_t *msize)
1029 uint32_t *msize)
1043 {
1030 {
1044 const char *data = databegin;
1031 const char *data = databegin;
1045 const char *meta;
1032 const char *meta;
1046
1033
1047 double mtime;
1034 double mtime;
1048 int16_t tz;
1035 int16_t tz;
1049 uint16_t flags;
1036 uint16_t flags;
1050 unsigned char nsuccs, nparents, nmetadata;
1037 unsigned char nsuccs, nparents, nmetadata;
1051 Py_ssize_t hashwidth = 20;
1038 Py_ssize_t hashwidth = 20;
1052
1039
1053 PyObject *prec = NULL, *parents = NULL, *succs = NULL;
1040 PyObject *prec = NULL, *parents = NULL, *succs = NULL;
1054 PyObject *metadata = NULL, *ret = NULL;
1041 PyObject *metadata = NULL, *ret = NULL;
1055 int i;
1042 int i;
1056
1043
1057 if (data + FM1_HEADER_SIZE > dataend) {
1044 if (data + FM1_HEADER_SIZE > dataend) {
1058 goto overflow;
1045 goto overflow;
1059 }
1046 }
1060
1047
1061 *msize = getbe32(data);
1048 *msize = getbe32(data);
1062 data += 4;
1049 data += 4;
1063 mtime = getbefloat64(data);
1050 mtime = getbefloat64(data);
1064 data += 8;
1051 data += 8;
1065 tz = getbeint16(data);
1052 tz = getbeint16(data);
1066 data += 2;
1053 data += 2;
1067 flags = getbeuint16(data);
1054 flags = getbeuint16(data);
1068 data += 2;
1055 data += 2;
1069
1056
1070 if (flags & USING_SHA_256) {
1057 if (flags & USING_SHA_256) {
1071 hashwidth = 32;
1058 hashwidth = 32;
1072 }
1059 }
1073
1060
1074 nsuccs = (unsigned char)(*data++);
1061 nsuccs = (unsigned char)(*data++);
1075 nparents = (unsigned char)(*data++);
1062 nparents = (unsigned char)(*data++);
1076 nmetadata = (unsigned char)(*data++);
1063 nmetadata = (unsigned char)(*data++);
1077
1064
1078 if (databegin + *msize > dataend) {
1065 if (databegin + *msize > dataend) {
1079 goto overflow;
1066 goto overflow;
1080 }
1067 }
1081 dataend = databegin + *msize; /* narrow down to marker size */
1068 dataend = databegin + *msize; /* narrow down to marker size */
1082
1069
1083 if (data + hashwidth > dataend) {
1070 if (data + hashwidth > dataend) {
1084 goto overflow;
1071 goto overflow;
1085 }
1072 }
1086 prec = PyBytes_FromStringAndSize(data, hashwidth);
1073 prec = PyBytes_FromStringAndSize(data, hashwidth);
1087 data += hashwidth;
1074 data += hashwidth;
1088 if (prec == NULL) {
1075 if (prec == NULL) {
1089 goto bail;
1076 goto bail;
1090 }
1077 }
1091
1078
1092 if (data + nsuccs * hashwidth > dataend) {
1079 if (data + nsuccs * hashwidth > dataend) {
1093 goto overflow;
1080 goto overflow;
1094 }
1081 }
1095 succs = readshas(data, nsuccs, hashwidth);
1082 succs = readshas(data, nsuccs, hashwidth);
1096 if (succs == NULL) {
1083 if (succs == NULL) {
1097 goto bail;
1084 goto bail;
1098 }
1085 }
1099 data += nsuccs * hashwidth;
1086 data += nsuccs * hashwidth;
1100
1087
1101 if (nparents == 1 || nparents == 2) {
1088 if (nparents == 1 || nparents == 2) {
1102 if (data + nparents * hashwidth > dataend) {
1089 if (data + nparents * hashwidth > dataend) {
1103 goto overflow;
1090 goto overflow;
1104 }
1091 }
1105 parents = readshas(data, nparents, hashwidth);
1092 parents = readshas(data, nparents, hashwidth);
1106 if (parents == NULL) {
1093 if (parents == NULL) {
1107 goto bail;
1094 goto bail;
1108 }
1095 }
1109 data += nparents * hashwidth;
1096 data += nparents * hashwidth;
1110 } else {
1097 } else {
1111 parents = Py_None;
1098 parents = Py_None;
1112 Py_INCREF(parents);
1099 Py_INCREF(parents);
1113 }
1100 }
1114
1101
1115 if (data + 2 * nmetadata > dataend) {
1102 if (data + 2 * nmetadata > dataend) {
1116 goto overflow;
1103 goto overflow;
1117 }
1104 }
1118 meta = data + (2 * nmetadata);
1105 meta = data + (2 * nmetadata);
1119 metadata = PyTuple_New(nmetadata);
1106 metadata = PyTuple_New(nmetadata);
1120 if (metadata == NULL) {
1107 if (metadata == NULL) {
1121 goto bail;
1108 goto bail;
1122 }
1109 }
1123 for (i = 0; i < nmetadata; i++) {
1110 for (i = 0; i < nmetadata; i++) {
1124 PyObject *tmp, *left = NULL, *right = NULL;
1111 PyObject *tmp, *left = NULL, *right = NULL;
1125 Py_ssize_t leftsize = (unsigned char)(*data++);
1112 Py_ssize_t leftsize = (unsigned char)(*data++);
1126 Py_ssize_t rightsize = (unsigned char)(*data++);
1113 Py_ssize_t rightsize = (unsigned char)(*data++);
1127 if (meta + leftsize + rightsize > dataend) {
1114 if (meta + leftsize + rightsize > dataend) {
1128 goto overflow;
1115 goto overflow;
1129 }
1116 }
1130 left = PyBytes_FromStringAndSize(meta, leftsize);
1117 left = PyBytes_FromStringAndSize(meta, leftsize);
1131 meta += leftsize;
1118 meta += leftsize;
1132 right = PyBytes_FromStringAndSize(meta, rightsize);
1119 right = PyBytes_FromStringAndSize(meta, rightsize);
1133 meta += rightsize;
1120 meta += rightsize;
1134 tmp = PyTuple_New(2);
1121 tmp = PyTuple_New(2);
1135 if (!left || !right || !tmp) {
1122 if (!left || !right || !tmp) {
1136 Py_XDECREF(left);
1123 Py_XDECREF(left);
1137 Py_XDECREF(right);
1124 Py_XDECREF(right);
1138 Py_XDECREF(tmp);
1125 Py_XDECREF(tmp);
1139 goto bail;
1126 goto bail;
1140 }
1127 }
1141 PyTuple_SET_ITEM(tmp, 0, left);
1128 PyTuple_SET_ITEM(tmp, 0, left);
1142 PyTuple_SET_ITEM(tmp, 1, right);
1129 PyTuple_SET_ITEM(tmp, 1, right);
1143 PyTuple_SET_ITEM(metadata, i, tmp);
1130 PyTuple_SET_ITEM(metadata, i, tmp);
1144 }
1131 }
1145 ret = Py_BuildValue("(OOHO(di)O)", prec, succs, flags, metadata, mtime,
1132 ret = Py_BuildValue("(OOHO(di)O)", prec, succs, flags, metadata, mtime,
1146 (int)tz * 60, parents);
1133 (int)tz * 60, parents);
1147 goto bail; /* return successfully */
1134 goto bail; /* return successfully */
1148
1135
1149 overflow:
1136 overflow:
1150 PyErr_SetString(PyExc_ValueError, "overflow in obsstore");
1137 PyErr_SetString(PyExc_ValueError, "overflow in obsstore");
1151 bail:
1138 bail:
1152 Py_XDECREF(prec);
1139 Py_XDECREF(prec);
1153 Py_XDECREF(succs);
1140 Py_XDECREF(succs);
1154 Py_XDECREF(metadata);
1141 Py_XDECREF(metadata);
1155 Py_XDECREF(parents);
1142 Py_XDECREF(parents);
1156 return ret;
1143 return ret;
1157 }
1144 }
1158
1145
1159 static PyObject *fm1readmarkers(PyObject *self, PyObject *args)
1146 static PyObject *fm1readmarkers(PyObject *self, PyObject *args)
1160 {
1147 {
1161 const char *data, *dataend;
1148 const char *data, *dataend;
1162 Py_ssize_t datalen, offset, stop;
1149 Py_ssize_t datalen, offset, stop;
1163 PyObject *markers = NULL;
1150 PyObject *markers = NULL;
1164
1151
1165 if (!PyArg_ParseTuple(args, PY23("s#nn", "y#nn"), &data, &datalen,
1152 if (!PyArg_ParseTuple(args, PY23("s#nn", "y#nn"), &data, &datalen,
1166 &offset, &stop)) {
1153 &offset, &stop)) {
1167 return NULL;
1154 return NULL;
1168 }
1155 }
1169 if (offset < 0) {
1156 if (offset < 0) {
1170 PyErr_SetString(PyExc_ValueError,
1157 PyErr_SetString(PyExc_ValueError,
1171 "invalid negative offset in fm1readmarkers");
1158 "invalid negative offset in fm1readmarkers");
1172 return NULL;
1159 return NULL;
1173 }
1160 }
1174 if (stop > datalen) {
1161 if (stop > datalen) {
1175 PyErr_SetString(
1162 PyErr_SetString(
1176 PyExc_ValueError,
1163 PyExc_ValueError,
1177 "stop longer than data length in fm1readmarkers");
1164 "stop longer than data length in fm1readmarkers");
1178 return NULL;
1165 return NULL;
1179 }
1166 }
1180 dataend = data + datalen;
1167 dataend = data + datalen;
1181 data += offset;
1168 data += offset;
1182 markers = PyList_New(0);
1169 markers = PyList_New(0);
1183 if (!markers) {
1170 if (!markers) {
1184 return NULL;
1171 return NULL;
1185 }
1172 }
1186 while (offset < stop) {
1173 while (offset < stop) {
1187 uint32_t msize;
1174 uint32_t msize;
1188 int error;
1175 int error;
1189 PyObject *record = fm1readmarker(data, dataend, &msize);
1176 PyObject *record = fm1readmarker(data, dataend, &msize);
1190 if (!record) {
1177 if (!record) {
1191 goto bail;
1178 goto bail;
1192 }
1179 }
1193 error = PyList_Append(markers, record);
1180 error = PyList_Append(markers, record);
1194 Py_DECREF(record);
1181 Py_DECREF(record);
1195 if (error) {
1182 if (error) {
1196 goto bail;
1183 goto bail;
1197 }
1184 }
1198 data += msize;
1185 data += msize;
1199 offset += msize;
1186 offset += msize;
1200 }
1187 }
1201 return markers;
1188 return markers;
1202 bail:
1189 bail:
1203 Py_DECREF(markers);
1190 Py_DECREF(markers);
1204 return NULL;
1191 return NULL;
1205 }
1192 }
1206
1193
1207 static char parsers_doc[] = "Efficient content parsing.";
1194 static char parsers_doc[] = "Efficient content parsing.";
1208
1195
1209 PyObject *encodedir(PyObject *self, PyObject *args);
1196 PyObject *encodedir(PyObject *self, PyObject *args);
1210 PyObject *pathencode(PyObject *self, PyObject *args);
1197 PyObject *pathencode(PyObject *self, PyObject *args);
1211 PyObject *lowerencode(PyObject *self, PyObject *args);
1198 PyObject *lowerencode(PyObject *self, PyObject *args);
1212 PyObject *parse_index2(PyObject *self, PyObject *args, PyObject *kwargs);
1199 PyObject *parse_index2(PyObject *self, PyObject *args, PyObject *kwargs);
1213
1200
1214 static PyMethodDef methods[] = {
1201 static PyMethodDef methods[] = {
1215 {"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"},
1202 {"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"},
1216 {"nonnormalotherparententries", nonnormalotherparententries, METH_VARARGS,
1203 {"nonnormalotherparententries", nonnormalotherparententries, METH_VARARGS,
1217 "create a set containing non-normal and other parent entries of given "
1204 "create a set containing non-normal and other parent entries of given "
1218 "dirstate\n"},
1205 "dirstate\n"},
1219 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
1206 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
1220 {"parse_index2", (PyCFunction)parse_index2, METH_VARARGS | METH_KEYWORDS,
1207 {"parse_index2", (PyCFunction)parse_index2, METH_VARARGS | METH_KEYWORDS,
1221 "parse a revlog index\n"},
1208 "parse a revlog index\n"},
1222 {"isasciistr", isasciistr, METH_VARARGS, "check if an ASCII string\n"},
1209 {"isasciistr", isasciistr, METH_VARARGS, "check if an ASCII string\n"},
1223 {"asciilower", asciilower, METH_VARARGS, "lowercase an ASCII string\n"},
1210 {"asciilower", asciilower, METH_VARARGS, "lowercase an ASCII string\n"},
1224 {"asciiupper", asciiupper, METH_VARARGS, "uppercase an ASCII string\n"},
1211 {"asciiupper", asciiupper, METH_VARARGS, "uppercase an ASCII string\n"},
1225 {"dict_new_presized", dict_new_presized, METH_VARARGS,
1212 {"dict_new_presized", dict_new_presized, METH_VARARGS,
1226 "construct a dict with an expected size\n"},
1213 "construct a dict with an expected size\n"},
1227 {"make_file_foldmap", make_file_foldmap, METH_VARARGS,
1214 {"make_file_foldmap", make_file_foldmap, METH_VARARGS,
1228 "make file foldmap\n"},
1215 "make file foldmap\n"},
1229 {"jsonescapeu8fast", jsonescapeu8fast, METH_VARARGS,
1216 {"jsonescapeu8fast", jsonescapeu8fast, METH_VARARGS,
1230 "escape a UTF-8 byte string to JSON (fast path)\n"},
1217 "escape a UTF-8 byte string to JSON (fast path)\n"},
1231 {"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"},
1218 {"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"},
1232 {"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"},
1219 {"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"},
1233 {"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"},
1220 {"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"},
1234 {"fm1readmarkers", fm1readmarkers, METH_VARARGS,
1221 {"fm1readmarkers", fm1readmarkers, METH_VARARGS,
1235 "parse v1 obsolete markers\n"},
1222 "parse v1 obsolete markers\n"},
1236 {NULL, NULL}};
1223 {NULL, NULL}};
1237
1224
1238 void dirs_module_init(PyObject *mod);
1225 void dirs_module_init(PyObject *mod);
1239 void manifest_module_init(PyObject *mod);
1226 void manifest_module_init(PyObject *mod);
1240 void revlog_module_init(PyObject *mod);
1227 void revlog_module_init(PyObject *mod);
1241
1228
1242 static const int version = 20;
1229 static const int version = 20;
1243
1230
1244 static void module_init(PyObject *mod)
1231 static void module_init(PyObject *mod)
1245 {
1232 {
1246 PyObject *capsule = NULL;
1233 PyObject *capsule = NULL;
1247 PyModule_AddIntConstant(mod, "version", version);
1234 PyModule_AddIntConstant(mod, "version", version);
1248
1235
1249 /* This module constant has two purposes. First, it lets us unit test
1236 /* This module constant has two purposes. First, it lets us unit test
1250 * the ImportError raised without hard-coding any error text. This
1237 * the ImportError raised without hard-coding any error text. This
1251 * means we can change the text in the future without breaking tests,
1238 * means we can change the text in the future without breaking tests,
1252 * even across changesets without a recompile. Second, its presence
1239 * even across changesets without a recompile. Second, its presence
1253 * can be used to determine whether the version-checking logic is
1240 * can be used to determine whether the version-checking logic is
1254 * present, which also helps in testing across changesets without a
1241 * present, which also helps in testing across changesets without a
1255 * recompile. Note that this means the pure-Python version of parsers
1242 * recompile. Note that this means the pure-Python version of parsers
1256 * should not have this module constant. */
1243 * should not have this module constant. */
1257 PyModule_AddStringConstant(mod, "versionerrortext", versionerrortext);
1244 PyModule_AddStringConstant(mod, "versionerrortext", versionerrortext);
1258
1245
1259 dirs_module_init(mod);
1246 dirs_module_init(mod);
1260 manifest_module_init(mod);
1247 manifest_module_init(mod);
1261 revlog_module_init(mod);
1248 revlog_module_init(mod);
1262
1249
1263 capsule = PyCapsule_New(
1250 capsule = PyCapsule_New(
1264 dirstate_item_from_v1_data,
1251 dirstate_item_from_v1_data,
1265 "mercurial.cext.parsers.make_dirstate_item_CAPI", NULL);
1252 "mercurial.cext.parsers.make_dirstate_item_CAPI", NULL);
1266 if (capsule != NULL)
1253 if (capsule != NULL)
1267 PyModule_AddObject(mod, "make_dirstate_item_CAPI", capsule);
1254 PyModule_AddObject(mod, "make_dirstate_item_CAPI", capsule);
1268
1255
1269 if (PyType_Ready(&dirstateItemType) < 0) {
1256 if (PyType_Ready(&dirstateItemType) < 0) {
1270 return;
1257 return;
1271 }
1258 }
1272 Py_INCREF(&dirstateItemType);
1259 Py_INCREF(&dirstateItemType);
1273 PyModule_AddObject(mod, "DirstateItem", (PyObject *)&dirstateItemType);
1260 PyModule_AddObject(mod, "DirstateItem", (PyObject *)&dirstateItemType);
1274 }
1261 }
1275
1262
1276 static int check_python_version(void)
1263 static int check_python_version(void)
1277 {
1264 {
1278 PyObject *sys = PyImport_ImportModule("sys"), *ver;
1265 PyObject *sys = PyImport_ImportModule("sys"), *ver;
1279 long hexversion;
1266 long hexversion;
1280 if (!sys) {
1267 if (!sys) {
1281 return -1;
1268 return -1;
1282 }
1269 }
1283 ver = PyObject_GetAttrString(sys, "hexversion");
1270 ver = PyObject_GetAttrString(sys, "hexversion");
1284 Py_DECREF(sys);
1271 Py_DECREF(sys);
1285 if (!ver) {
1272 if (!ver) {
1286 return -1;
1273 return -1;
1287 }
1274 }
1288 hexversion = PyInt_AsLong(ver);
1275 hexversion = PyInt_AsLong(ver);
1289 Py_DECREF(ver);
1276 Py_DECREF(ver);
1290 /* sys.hexversion is a 32-bit number by default, so the -1 case
1277 /* sys.hexversion is a 32-bit number by default, so the -1 case
1291 * should only occur in unusual circumstances (e.g. if sys.hexversion
1278 * should only occur in unusual circumstances (e.g. if sys.hexversion
1292 * is manually set to an invalid value). */
1279 * is manually set to an invalid value). */
1293 if ((hexversion == -1) || (hexversion >> 16 != PY_VERSION_HEX >> 16)) {
1280 if ((hexversion == -1) || (hexversion >> 16 != PY_VERSION_HEX >> 16)) {
1294 PyErr_Format(PyExc_ImportError,
1281 PyErr_Format(PyExc_ImportError,
1295 "%s: The Mercurial extension "
1282 "%s: The Mercurial extension "
1296 "modules were compiled with Python " PY_VERSION
1283 "modules were compiled with Python " PY_VERSION
1297 ", but "
1284 ", but "
1298 "Mercurial is currently using Python with "
1285 "Mercurial is currently using Python with "
1299 "sys.hexversion=%ld: "
1286 "sys.hexversion=%ld: "
1300 "Python %s\n at: %s",
1287 "Python %s\n at: %s",
1301 versionerrortext, hexversion, Py_GetVersion(),
1288 versionerrortext, hexversion, Py_GetVersion(),
1302 Py_GetProgramFullPath());
1289 Py_GetProgramFullPath());
1303 return -1;
1290 return -1;
1304 }
1291 }
1305 return 0;
1292 return 0;
1306 }
1293 }
1307
1294
1308 #ifdef IS_PY3K
1295 #ifdef IS_PY3K
1309 static struct PyModuleDef parsers_module = {PyModuleDef_HEAD_INIT, "parsers",
1296 static struct PyModuleDef parsers_module = {PyModuleDef_HEAD_INIT, "parsers",
1310 parsers_doc, -1, methods};
1297 parsers_doc, -1, methods};
1311
1298
1312 PyMODINIT_FUNC PyInit_parsers(void)
1299 PyMODINIT_FUNC PyInit_parsers(void)
1313 {
1300 {
1314 PyObject *mod;
1301 PyObject *mod;
1315
1302
1316 if (check_python_version() == -1)
1303 if (check_python_version() == -1)
1317 return NULL;
1304 return NULL;
1318 mod = PyModule_Create(&parsers_module);
1305 mod = PyModule_Create(&parsers_module);
1319 module_init(mod);
1306 module_init(mod);
1320 return mod;
1307 return mod;
1321 }
1308 }
1322 #else
1309 #else
1323 PyMODINIT_FUNC initparsers(void)
1310 PyMODINIT_FUNC initparsers(void)
1324 {
1311 {
1325 PyObject *mod;
1312 PyObject *mod;
1326
1313
1327 if (check_python_version() == -1) {
1314 if (check_python_version() == -1) {
1328 return;
1315 return;
1329 }
1316 }
1330 mod = Py_InitModule3("parsers", methods, parsers_doc);
1317 mod = Py_InitModule3("parsers", methods, parsers_doc);
1331 module_init(mod);
1318 module_init(mod);
1332 }
1319 }
1333 #endif
1320 #endif
@@ -1,83 +1,82 b''
1 /*
1 /*
2 util.h - utility functions for interfacing with the various python APIs.
2 util.h - utility functions for interfacing with the various python APIs.
3
3
4 This software may be used and distributed according to the terms of
4 This software may be used and distributed according to the terms of
5 the GNU General Public License, incorporated herein by reference.
5 the GNU General Public License, incorporated herein by reference.
6 */
6 */
7
7
8 #ifndef _HG_UTIL_H_
8 #ifndef _HG_UTIL_H_
9 #define _HG_UTIL_H_
9 #define _HG_UTIL_H_
10
10
11 #include "compat.h"
11 #include "compat.h"
12
12
13 #if PY_MAJOR_VERSION >= 3
13 #if PY_MAJOR_VERSION >= 3
14 #define IS_PY3K
14 #define IS_PY3K
15 #endif
15 #endif
16
16
17 /* helper to switch things like string literal depending on Python version */
17 /* helper to switch things like string literal depending on Python version */
18 #ifdef IS_PY3K
18 #ifdef IS_PY3K
19 #define PY23(py2, py3) py3
19 #define PY23(py2, py3) py3
20 #else
20 #else
21 #define PY23(py2, py3) py2
21 #define PY23(py2, py3) py2
22 #endif
22 #endif
23
23
24 /* clang-format off */
24 /* clang-format off */
25 typedef struct {
25 typedef struct {
26 PyObject_HEAD
26 PyObject_HEAD
27 unsigned char flags;
27 unsigned char flags;
28 int mode;
28 int mode;
29 int size;
29 int size;
30 int mtime;
30 int mtime;
31 } dirstateItemObject;
31 } dirstateItemObject;
32 /* clang-format on */
32 /* clang-format on */
33
33
34 static const unsigned char dirstate_flag_wc_tracked = 1;
34 static const unsigned char dirstate_flag_wc_tracked = 1;
35 static const unsigned char dirstate_flag_p1_tracked = 1 << 1;
35 static const unsigned char dirstate_flag_p1_tracked = 1 << 1;
36 static const unsigned char dirstate_flag_p2_tracked = 1 << 2;
36 static const unsigned char dirstate_flag_p2_tracked = 1 << 2;
37 static const unsigned char dirstate_flag_possibly_dirty = 1 << 3;
37 static const unsigned char dirstate_flag_possibly_dirty = 1 << 3;
38 static const unsigned char dirstate_flag_merged = 1 << 4;
38 static const unsigned char dirstate_flag_merged = 1 << 4;
39 static const unsigned char dirstate_flag_clean_p1 = 1 << 5;
39 static const unsigned char dirstate_flag_clean_p1 = 1 << 5;
40 static const unsigned char dirstate_flag_clean_p2 = 1 << 6;
40 static const unsigned char dirstate_flag_clean_p2 = 1 << 6;
41 static const unsigned char dirstate_flag_rust_special = 1 << 7;
42
41
43 extern PyTypeObject dirstateItemType;
42 extern PyTypeObject dirstateItemType;
44 #define dirstate_tuple_check(op) (Py_TYPE(op) == &dirstateItemType)
43 #define dirstate_tuple_check(op) (Py_TYPE(op) == &dirstateItemType)
45
44
46 #ifndef MIN
45 #ifndef MIN
47 #define MIN(a, b) (((a) < (b)) ? (a) : (b))
46 #define MIN(a, b) (((a) < (b)) ? (a) : (b))
48 #endif
47 #endif
49 /* VC9 doesn't include bool and lacks stdbool.h based on my searching */
48 /* VC9 doesn't include bool and lacks stdbool.h based on my searching */
50 #if defined(_MSC_VER) || __STDC_VERSION__ < 199901L
49 #if defined(_MSC_VER) || __STDC_VERSION__ < 199901L
51 #define true 1
50 #define true 1
52 #define false 0
51 #define false 0
53 typedef unsigned char bool;
52 typedef unsigned char bool;
54 #else
53 #else
55 #include <stdbool.h>
54 #include <stdbool.h>
56 #endif
55 #endif
57
56
58 static inline PyObject *_dict_new_presized(Py_ssize_t expected_size)
57 static inline PyObject *_dict_new_presized(Py_ssize_t expected_size)
59 {
58 {
60 /* _PyDict_NewPresized expects a minused parameter, but it actually
59 /* _PyDict_NewPresized expects a minused parameter, but it actually
61 creates a dictionary that's the nearest power of two bigger than the
60 creates a dictionary that's the nearest power of two bigger than the
62 parameter. For example, with the initial minused = 1000, the
61 parameter. For example, with the initial minused = 1000, the
63 dictionary created has size 1024. Of course in a lot of cases that
62 dictionary created has size 1024. Of course in a lot of cases that
64 can be greater than the maximum load factor Python's dict object
63 can be greater than the maximum load factor Python's dict object
65 expects (= 2/3), so as soon as we cross the threshold we'll resize
64 expects (= 2/3), so as soon as we cross the threshold we'll resize
66 anyway. So create a dictionary that's at least 3/2 the size. */
65 anyway. So create a dictionary that's at least 3/2 the size. */
67 return _PyDict_NewPresized(((1 + expected_size) / 2) * 3);
66 return _PyDict_NewPresized(((1 + expected_size) / 2) * 3);
68 }
67 }
69
68
70 /* Convert a PyInt or PyLong to a long. Returns false if there is an
69 /* Convert a PyInt or PyLong to a long. Returns false if there is an
71 error, in which case an exception will already have been set. */
70 error, in which case an exception will already have been set. */
72 static inline bool pylong_to_long(PyObject *pylong, long *out)
71 static inline bool pylong_to_long(PyObject *pylong, long *out)
73 {
72 {
74 *out = PyLong_AsLong(pylong);
73 *out = PyLong_AsLong(pylong);
75 /* Fast path to avoid hitting PyErr_Occurred if the value was obviously
74 /* Fast path to avoid hitting PyErr_Occurred if the value was obviously
76 * not an error. */
75 * not an error. */
77 if (*out != -1) {
76 if (*out != -1) {
78 return true;
77 return true;
79 }
78 }
80 return PyErr_Occurred() == NULL;
79 return PyErr_Occurred() == NULL;
81 }
80 }
82
81
83 #endif /* _HG_UTIL_H_ */
82 #endif /* _HG_UTIL_H_ */
@@ -1,122 +1,112 b''
1 // dirstate.rs
1 // dirstate.rs
2 //
2 //
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 //
4 //
5 // This software may be used and distributed according to the terms of the
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
6 // GNU General Public License version 2 or any later version.
7
7
8 //! Bindings for the `hg::dirstate` module provided by the
8 //! Bindings for the `hg::dirstate` module provided by the
9 //! `hg-core` package.
9 //! `hg-core` package.
10 //!
10 //!
11 //! From Python, this will be seen as `mercurial.rustext.dirstate`
11 //! From Python, this will be seen as `mercurial.rustext.dirstate`
12 mod copymap;
12 mod copymap;
13 mod dirs_multiset;
13 mod dirs_multiset;
14 mod dirstate_map;
14 mod dirstate_map;
15 mod non_normal_entries;
15 mod non_normal_entries;
16 mod status;
16 mod status;
17 use crate::{
17 use crate::{
18 dirstate::{
18 dirstate::{
19 dirs_multiset::Dirs, dirstate_map::DirstateMap, status::status_wrapper,
19 dirs_multiset::Dirs, dirstate_map::DirstateMap, status::status_wrapper,
20 },
20 },
21 exceptions,
21 exceptions,
22 };
22 };
23 use cpython::{
23 use cpython::{
24 PyBytes, PyDict, PyErr, PyList, PyModule, PyObject, PyResult, Python,
24 PyBytes, PyDict, PyErr, PyList, PyModule, PyObject, PyResult, Python,
25 };
25 };
26 use hg::dirstate_tree::on_disk::V2_FORMAT_MARKER;
26 use hg::dirstate_tree::on_disk::V2_FORMAT_MARKER;
27 use hg::DirstateEntry;
27 use hg::DirstateEntry;
28 use libc::{c_char, c_int};
28 use libc::{c_char, c_int};
29
29
30 // C code uses a custom `dirstate_tuple` type, checks in multiple instances
30 // C code uses a custom `dirstate_tuple` type, checks in multiple instances
31 // for this type, and raises a Python `Exception` if the check does not pass.
31 // for this type, and raises a Python `Exception` if the check does not pass.
32 // Because this type differs only in name from the regular Python tuple, it
32 // Because this type differs only in name from the regular Python tuple, it
33 // would be a good idea in the near future to remove it entirely to allow
33 // would be a good idea in the near future to remove it entirely to allow
34 // for a pure Python tuple of the same effective structure to be used,
34 // for a pure Python tuple of the same effective structure to be used,
35 // rendering this type and the capsule below useless.
35 // rendering this type and the capsule below useless.
36 py_capsule_fn!(
36 py_capsule_fn!(
37 from mercurial.cext.parsers import make_dirstate_item_CAPI
37 from mercurial.cext.parsers import make_dirstate_item_CAPI
38 as make_dirstate_item_capi
38 as make_dirstate_item_capi
39 signature (
39 signature (
40 state: c_char,
40 state: c_char,
41 mode: c_int,
41 mode: c_int,
42 size: c_int,
42 size: c_int,
43 mtime: c_int,
43 mtime: c_int,
44 ) -> *mut RawPyObject
44 ) -> *mut RawPyObject
45 );
45 );
46
46
47 pub fn make_dirstate_item(
47 pub fn make_dirstate_item(
48 py: Python,
48 py: Python,
49 entry: &DirstateEntry,
49 entry: &DirstateEntry,
50 ) -> PyResult<PyObject> {
50 ) -> PyResult<PyObject> {
51 // Explicitly go through u8 first, then cast to platform-specific `c_char`
51 // Explicitly go through u8 first, then cast to platform-specific `c_char`
52 // because Into<u8> has a specific implementation while `as c_char` would
52 // because Into<u8> has a specific implementation while `as c_char` would
53 // just do a naive enum cast.
53 // just do a naive enum cast.
54 let state_code: u8 = entry.state().into();
54 let state_code: u8 = entry.state().into();
55 make_dirstate_item_raw(
56 py,
57 state_code,
58 entry.mode(),
59 entry.size(),
60 entry.mtime(),
61 )
62 }
63
55
64 pub fn make_dirstate_item_raw(
65 py: Python,
66 state: u8,
67 mode: i32,
68 size: i32,
69 mtime: i32,
70 ) -> PyResult<PyObject> {
71 let make = make_dirstate_item_capi::retrieve(py)?;
56 let make = make_dirstate_item_capi::retrieve(py)?;
72 let maybe_obj = unsafe {
57 let maybe_obj = unsafe {
73 let ptr = make(state as c_char, mode, size, mtime);
58 let ptr = make(
59 state_code as c_char,
60 entry.mode(),
61 entry.size(),
62 entry.mtime(),
63 );
74 PyObject::from_owned_ptr_opt(py, ptr)
64 PyObject::from_owned_ptr_opt(py, ptr)
75 };
65 };
76 maybe_obj.ok_or_else(|| PyErr::fetch(py))
66 maybe_obj.ok_or_else(|| PyErr::fetch(py))
77 }
67 }
78
68
79 /// Create the module, with `__package__` given from parent
69 /// Create the module, with `__package__` given from parent
80 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
70 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
81 let dotted_name = &format!("{}.dirstate", package);
71 let dotted_name = &format!("{}.dirstate", package);
82 let m = PyModule::new(py, dotted_name)?;
72 let m = PyModule::new(py, dotted_name)?;
83
73
84 env_logger::init();
74 env_logger::init();
85
75
86 m.add(py, "__package__", package)?;
76 m.add(py, "__package__", package)?;
87 m.add(py, "__doc__", "Dirstate - Rust implementation")?;
77 m.add(py, "__doc__", "Dirstate - Rust implementation")?;
88
78
89 m.add(
79 m.add(
90 py,
80 py,
91 "FallbackError",
81 "FallbackError",
92 py.get_type::<exceptions::FallbackError>(),
82 py.get_type::<exceptions::FallbackError>(),
93 )?;
83 )?;
94 m.add_class::<Dirs>(py)?;
84 m.add_class::<Dirs>(py)?;
95 m.add_class::<DirstateMap>(py)?;
85 m.add_class::<DirstateMap>(py)?;
96 m.add(py, "V2_FORMAT_MARKER", PyBytes::new(py, V2_FORMAT_MARKER))?;
86 m.add(py, "V2_FORMAT_MARKER", PyBytes::new(py, V2_FORMAT_MARKER))?;
97 m.add(
87 m.add(
98 py,
88 py,
99 "status",
89 "status",
100 py_fn!(
90 py_fn!(
101 py,
91 py,
102 status_wrapper(
92 status_wrapper(
103 dmap: DirstateMap,
93 dmap: DirstateMap,
104 root_dir: PyObject,
94 root_dir: PyObject,
105 matcher: PyObject,
95 matcher: PyObject,
106 ignorefiles: PyList,
96 ignorefiles: PyList,
107 check_exec: bool,
97 check_exec: bool,
108 last_normal_time: i64,
98 last_normal_time: i64,
109 list_clean: bool,
99 list_clean: bool,
110 list_ignored: bool,
100 list_ignored: bool,
111 list_unknown: bool,
101 list_unknown: bool,
112 collect_traversed_dirs: bool
102 collect_traversed_dirs: bool
113 )
103 )
114 ),
104 ),
115 )?;
105 )?;
116
106
117 let sys = PyModule::import(py, "sys")?;
107 let sys = PyModule::import(py, "sys")?;
118 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
108 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
119 sys_modules.set_item(py, dotted_name, &m)?;
109 sys_modules.set_item(py, dotted_name, &m)?;
120
110
121 Ok(m)
111 Ok(m)
122 }
112 }
General Comments 0
You need to be logged in to leave comments. Login now