##// END OF EJS Templates
cext: unconditionalize PYLONG_VALUE()...
Gregory Szorc -
r49674:bf6cdda9 default
parent child Browse files
Show More
@@ -1,326 +1,322 b''
1 /*
1 /*
2 dirs.c - dynamic directory diddling for dirstates
2 dirs.c - dynamic directory diddling for dirstates
3
3
4 Copyright 2013 Facebook
4 Copyright 2013 Facebook
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 <string.h>
12 #include <string.h>
13
13
14 #include "util.h"
14 #include "util.h"
15
15
16 #ifdef IS_PY3K
17 #define PYLONG_VALUE(o) ((PyLongObject *)o)->ob_digit[0]
16 #define PYLONG_VALUE(o) ((PyLongObject *)o)->ob_digit[0]
18 #else
19 #define PYLONG_VALUE(o) PyInt_AS_LONG(o)
20 #endif
21
17
22 /*
18 /*
23 * This is a multiset of directory names, built from the files that
19 * This is a multiset of directory names, built from the files that
24 * appear in a dirstate or manifest.
20 * appear in a dirstate or manifest.
25 *
21 *
26 * A few implementation notes:
22 * A few implementation notes:
27 *
23 *
28 * We modify Python integers for refcounting, but those integers are
24 * We modify Python integers for refcounting, but those integers are
29 * never visible to Python code.
25 * never visible to Python code.
30 */
26 */
31 /* clang-format off */
27 /* clang-format off */
32 typedef struct {
28 typedef struct {
33 PyObject_HEAD
29 PyObject_HEAD
34 PyObject *dict;
30 PyObject *dict;
35 } dirsObject;
31 } dirsObject;
36 /* clang-format on */
32 /* clang-format on */
37
33
38 static inline Py_ssize_t _finddir(const char *path, Py_ssize_t pos)
34 static inline Py_ssize_t _finddir(const char *path, Py_ssize_t pos)
39 {
35 {
40 while (pos != -1) {
36 while (pos != -1) {
41 if (path[pos] == '/')
37 if (path[pos] == '/')
42 break;
38 break;
43 pos -= 1;
39 pos -= 1;
44 }
40 }
45 if (pos == -1) {
41 if (pos == -1) {
46 return 0;
42 return 0;
47 }
43 }
48
44
49 return pos;
45 return pos;
50 }
46 }
51
47
52 /* Mercurial will fail to run on directory hierarchies deeper than
48 /* Mercurial will fail to run on directory hierarchies deeper than
53 * this constant, so we should try and keep this constant as big as
49 * this constant, so we should try and keep this constant as big as
54 * possible.
50 * possible.
55 */
51 */
56 #define MAX_DIRS_DEPTH 2048
52 #define MAX_DIRS_DEPTH 2048
57
53
58 static int _addpath(PyObject *dirs, PyObject *path)
54 static int _addpath(PyObject *dirs, PyObject *path)
59 {
55 {
60 const char *cpath = PyBytes_AS_STRING(path);
56 const char *cpath = PyBytes_AS_STRING(path);
61 Py_ssize_t pos = PyBytes_GET_SIZE(path);
57 Py_ssize_t pos = PyBytes_GET_SIZE(path);
62 PyObject *key = NULL;
58 PyObject *key = NULL;
63 int ret = -1;
59 int ret = -1;
64 size_t num_slashes = 0;
60 size_t num_slashes = 0;
65
61
66 /* This loop is super critical for performance. That's why we inline
62 /* This loop is super critical for performance. That's why we inline
67 * access to Python structs instead of going through a supported API.
63 * access to Python structs instead of going through a supported API.
68 * The implementation, therefore, is heavily dependent on CPython
64 * The implementation, therefore, is heavily dependent on CPython
69 * implementation details. We also commit violations of the Python
65 * implementation details. We also commit violations of the Python
70 * "protocol" such as mutating immutable objects. But since we only
66 * "protocol" such as mutating immutable objects. But since we only
71 * mutate objects created in this function or in other well-defined
67 * mutate objects created in this function or in other well-defined
72 * locations, the references are known so these violations should go
68 * locations, the references are known so these violations should go
73 * unnoticed. */
69 * unnoticed. */
74 while ((pos = _finddir(cpath, pos - 1)) != -1) {
70 while ((pos = _finddir(cpath, pos - 1)) != -1) {
75 PyObject *val;
71 PyObject *val;
76 ++num_slashes;
72 ++num_slashes;
77 if (num_slashes > MAX_DIRS_DEPTH) {
73 if (num_slashes > MAX_DIRS_DEPTH) {
78 PyErr_SetString(PyExc_ValueError,
74 PyErr_SetString(PyExc_ValueError,
79 "Directory hierarchy too deep.");
75 "Directory hierarchy too deep.");
80 goto bail;
76 goto bail;
81 }
77 }
82
78
83 /* Sniff for trailing slashes, a marker of an invalid input. */
79 /* Sniff for trailing slashes, a marker of an invalid input. */
84 if (pos > 0 && cpath[pos - 1] == '/') {
80 if (pos > 0 && cpath[pos - 1] == '/') {
85 PyErr_SetString(
81 PyErr_SetString(
86 PyExc_ValueError,
82 PyExc_ValueError,
87 "found invalid consecutive slashes in path");
83 "found invalid consecutive slashes in path");
88 goto bail;
84 goto bail;
89 }
85 }
90
86
91 key = PyBytes_FromStringAndSize(cpath, pos);
87 key = PyBytes_FromStringAndSize(cpath, pos);
92 if (key == NULL)
88 if (key == NULL)
93 goto bail;
89 goto bail;
94
90
95 val = PyDict_GetItem(dirs, key);
91 val = PyDict_GetItem(dirs, key);
96 if (val != NULL) {
92 if (val != NULL) {
97 PYLONG_VALUE(val) += 1;
93 PYLONG_VALUE(val) += 1;
98 Py_CLEAR(key);
94 Py_CLEAR(key);
99 break;
95 break;
100 }
96 }
101
97
102 /* Force Python to not reuse a small shared int. */
98 /* Force Python to not reuse a small shared int. */
103 val = PyLong_FromLong(0x1eadbeef);
99 val = PyLong_FromLong(0x1eadbeef);
104
100
105 if (val == NULL)
101 if (val == NULL)
106 goto bail;
102 goto bail;
107
103
108 PYLONG_VALUE(val) = 1;
104 PYLONG_VALUE(val) = 1;
109 ret = PyDict_SetItem(dirs, key, val);
105 ret = PyDict_SetItem(dirs, key, val);
110 Py_DECREF(val);
106 Py_DECREF(val);
111 if (ret == -1)
107 if (ret == -1)
112 goto bail;
108 goto bail;
113 Py_CLEAR(key);
109 Py_CLEAR(key);
114 }
110 }
115 ret = 0;
111 ret = 0;
116
112
117 bail:
113 bail:
118 Py_XDECREF(key);
114 Py_XDECREF(key);
119
115
120 return ret;
116 return ret;
121 }
117 }
122
118
123 static int _delpath(PyObject *dirs, PyObject *path)
119 static int _delpath(PyObject *dirs, PyObject *path)
124 {
120 {
125 char *cpath = PyBytes_AS_STRING(path);
121 char *cpath = PyBytes_AS_STRING(path);
126 Py_ssize_t pos = PyBytes_GET_SIZE(path);
122 Py_ssize_t pos = PyBytes_GET_SIZE(path);
127 PyObject *key = NULL;
123 PyObject *key = NULL;
128 int ret = -1;
124 int ret = -1;
129
125
130 while ((pos = _finddir(cpath, pos - 1)) != -1) {
126 while ((pos = _finddir(cpath, pos - 1)) != -1) {
131 PyObject *val;
127 PyObject *val;
132
128
133 key = PyBytes_FromStringAndSize(cpath, pos);
129 key = PyBytes_FromStringAndSize(cpath, pos);
134
130
135 if (key == NULL)
131 if (key == NULL)
136 goto bail;
132 goto bail;
137
133
138 val = PyDict_GetItem(dirs, key);
134 val = PyDict_GetItem(dirs, key);
139 if (val == NULL) {
135 if (val == NULL) {
140 PyErr_SetString(PyExc_ValueError,
136 PyErr_SetString(PyExc_ValueError,
141 "expected a value, found none");
137 "expected a value, found none");
142 goto bail;
138 goto bail;
143 }
139 }
144
140
145 if (--PYLONG_VALUE(val) <= 0) {
141 if (--PYLONG_VALUE(val) <= 0) {
146 if (PyDict_DelItem(dirs, key) == -1)
142 if (PyDict_DelItem(dirs, key) == -1)
147 goto bail;
143 goto bail;
148 } else
144 } else
149 break;
145 break;
150 Py_CLEAR(key);
146 Py_CLEAR(key);
151 }
147 }
152 ret = 0;
148 ret = 0;
153
149
154 bail:
150 bail:
155 Py_XDECREF(key);
151 Py_XDECREF(key);
156
152
157 return ret;
153 return ret;
158 }
154 }
159
155
160 static int dirs_fromdict(PyObject *dirs, PyObject *source, bool only_tracked)
156 static int dirs_fromdict(PyObject *dirs, PyObject *source, bool only_tracked)
161 {
157 {
162 PyObject *key, *value;
158 PyObject *key, *value;
163 Py_ssize_t pos = 0;
159 Py_ssize_t pos = 0;
164
160
165 while (PyDict_Next(source, &pos, &key, &value)) {
161 while (PyDict_Next(source, &pos, &key, &value)) {
166 if (!PyBytes_Check(key)) {
162 if (!PyBytes_Check(key)) {
167 PyErr_SetString(PyExc_TypeError, "expected string key");
163 PyErr_SetString(PyExc_TypeError, "expected string key");
168 return -1;
164 return -1;
169 }
165 }
170 if (only_tracked) {
166 if (only_tracked) {
171 if (!dirstate_tuple_check(value)) {
167 if (!dirstate_tuple_check(value)) {
172 PyErr_SetString(PyExc_TypeError,
168 PyErr_SetString(PyExc_TypeError,
173 "expected a dirstate tuple");
169 "expected a dirstate tuple");
174 return -1;
170 return -1;
175 }
171 }
176 if (!(((dirstateItemObject *)value)->flags &
172 if (!(((dirstateItemObject *)value)->flags &
177 dirstate_flag_wc_tracked))
173 dirstate_flag_wc_tracked))
178 continue;
174 continue;
179 }
175 }
180
176
181 if (_addpath(dirs, key) == -1)
177 if (_addpath(dirs, key) == -1)
182 return -1;
178 return -1;
183 }
179 }
184
180
185 return 0;
181 return 0;
186 }
182 }
187
183
188 static int dirs_fromiter(PyObject *dirs, PyObject *source)
184 static int dirs_fromiter(PyObject *dirs, PyObject *source)
189 {
185 {
190 PyObject *iter, *item = NULL;
186 PyObject *iter, *item = NULL;
191 int ret;
187 int ret;
192
188
193 iter = PyObject_GetIter(source);
189 iter = PyObject_GetIter(source);
194 if (iter == NULL)
190 if (iter == NULL)
195 return -1;
191 return -1;
196
192
197 while ((item = PyIter_Next(iter)) != NULL) {
193 while ((item = PyIter_Next(iter)) != NULL) {
198 if (!PyBytes_Check(item)) {
194 if (!PyBytes_Check(item)) {
199 PyErr_SetString(PyExc_TypeError, "expected string");
195 PyErr_SetString(PyExc_TypeError, "expected string");
200 break;
196 break;
201 }
197 }
202
198
203 if (_addpath(dirs, item) == -1)
199 if (_addpath(dirs, item) == -1)
204 break;
200 break;
205 Py_CLEAR(item);
201 Py_CLEAR(item);
206 }
202 }
207
203
208 ret = PyErr_Occurred() ? -1 : 0;
204 ret = PyErr_Occurred() ? -1 : 0;
209 Py_DECREF(iter);
205 Py_DECREF(iter);
210 Py_XDECREF(item);
206 Py_XDECREF(item);
211 return ret;
207 return ret;
212 }
208 }
213
209
214 /*
210 /*
215 * Calculate a refcounted set of directory names for the files in a
211 * Calculate a refcounted set of directory names for the files in a
216 * dirstate.
212 * dirstate.
217 */
213 */
218 static int dirs_init(dirsObject *self, PyObject *args, PyObject *kwargs)
214 static int dirs_init(dirsObject *self, PyObject *args, PyObject *kwargs)
219 {
215 {
220 PyObject *dirs = NULL, *source = NULL;
216 PyObject *dirs = NULL, *source = NULL;
221 int only_tracked = 0;
217 int only_tracked = 0;
222 int ret = -1;
218 int ret = -1;
223 static char *keywords_name[] = {"map", "only_tracked", NULL};
219 static char *keywords_name[] = {"map", "only_tracked", NULL};
224
220
225 self->dict = NULL;
221 self->dict = NULL;
226
222
227 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|Oi:__init__",
223 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|Oi:__init__",
228 keywords_name, &source, &only_tracked))
224 keywords_name, &source, &only_tracked))
229 return -1;
225 return -1;
230
226
231 dirs = PyDict_New();
227 dirs = PyDict_New();
232
228
233 if (dirs == NULL)
229 if (dirs == NULL)
234 return -1;
230 return -1;
235
231
236 if (source == NULL)
232 if (source == NULL)
237 ret = 0;
233 ret = 0;
238 else if (PyDict_Check(source))
234 else if (PyDict_Check(source))
239 ret = dirs_fromdict(dirs, source, (bool)only_tracked);
235 ret = dirs_fromdict(dirs, source, (bool)only_tracked);
240 else if (only_tracked)
236 else if (only_tracked)
241 PyErr_SetString(PyExc_ValueError,
237 PyErr_SetString(PyExc_ValueError,
242 "`only_tracked` is only supported "
238 "`only_tracked` is only supported "
243 "with a dict source");
239 "with a dict source");
244 else
240 else
245 ret = dirs_fromiter(dirs, source);
241 ret = dirs_fromiter(dirs, source);
246
242
247 if (ret == -1)
243 if (ret == -1)
248 Py_XDECREF(dirs);
244 Py_XDECREF(dirs);
249 else
245 else
250 self->dict = dirs;
246 self->dict = dirs;
251
247
252 return ret;
248 return ret;
253 }
249 }
254
250
255 PyObject *dirs_addpath(dirsObject *self, PyObject *args)
251 PyObject *dirs_addpath(dirsObject *self, PyObject *args)
256 {
252 {
257 PyObject *path;
253 PyObject *path;
258
254
259 if (!PyArg_ParseTuple(args, "O!:addpath", &PyBytes_Type, &path))
255 if (!PyArg_ParseTuple(args, "O!:addpath", &PyBytes_Type, &path))
260 return NULL;
256 return NULL;
261
257
262 if (_addpath(self->dict, path) == -1)
258 if (_addpath(self->dict, path) == -1)
263 return NULL;
259 return NULL;
264
260
265 Py_RETURN_NONE;
261 Py_RETURN_NONE;
266 }
262 }
267
263
268 static PyObject *dirs_delpath(dirsObject *self, PyObject *args)
264 static PyObject *dirs_delpath(dirsObject *self, PyObject *args)
269 {
265 {
270 PyObject *path;
266 PyObject *path;
271
267
272 if (!PyArg_ParseTuple(args, "O!:delpath", &PyBytes_Type, &path))
268 if (!PyArg_ParseTuple(args, "O!:delpath", &PyBytes_Type, &path))
273 return NULL;
269 return NULL;
274
270
275 if (_delpath(self->dict, path) == -1)
271 if (_delpath(self->dict, path) == -1)
276 return NULL;
272 return NULL;
277
273
278 Py_RETURN_NONE;
274 Py_RETURN_NONE;
279 }
275 }
280
276
281 static int dirs_contains(dirsObject *self, PyObject *value)
277 static int dirs_contains(dirsObject *self, PyObject *value)
282 {
278 {
283 return PyBytes_Check(value) ? PyDict_Contains(self->dict, value) : 0;
279 return PyBytes_Check(value) ? PyDict_Contains(self->dict, value) : 0;
284 }
280 }
285
281
286 static void dirs_dealloc(dirsObject *self)
282 static void dirs_dealloc(dirsObject *self)
287 {
283 {
288 Py_XDECREF(self->dict);
284 Py_XDECREF(self->dict);
289 PyObject_Del(self);
285 PyObject_Del(self);
290 }
286 }
291
287
292 static PyObject *dirs_iter(dirsObject *self)
288 static PyObject *dirs_iter(dirsObject *self)
293 {
289 {
294 return PyObject_GetIter(self->dict);
290 return PyObject_GetIter(self->dict);
295 }
291 }
296
292
297 static PySequenceMethods dirs_sequence_methods;
293 static PySequenceMethods dirs_sequence_methods;
298
294
299 static PyMethodDef dirs_methods[] = {
295 static PyMethodDef dirs_methods[] = {
300 {"addpath", (PyCFunction)dirs_addpath, METH_VARARGS, "add a path"},
296 {"addpath", (PyCFunction)dirs_addpath, METH_VARARGS, "add a path"},
301 {"delpath", (PyCFunction)dirs_delpath, METH_VARARGS, "remove a path"},
297 {"delpath", (PyCFunction)dirs_delpath, METH_VARARGS, "remove a path"},
302 {NULL} /* Sentinel */
298 {NULL} /* Sentinel */
303 };
299 };
304
300
305 static PyTypeObject dirsType = {PyVarObject_HEAD_INIT(NULL, 0)};
301 static PyTypeObject dirsType = {PyVarObject_HEAD_INIT(NULL, 0)};
306
302
307 void dirs_module_init(PyObject *mod)
303 void dirs_module_init(PyObject *mod)
308 {
304 {
309 dirs_sequence_methods.sq_contains = (objobjproc)dirs_contains;
305 dirs_sequence_methods.sq_contains = (objobjproc)dirs_contains;
310 dirsType.tp_name = "parsers.dirs";
306 dirsType.tp_name = "parsers.dirs";
311 dirsType.tp_new = PyType_GenericNew;
307 dirsType.tp_new = PyType_GenericNew;
312 dirsType.tp_basicsize = sizeof(dirsObject);
308 dirsType.tp_basicsize = sizeof(dirsObject);
313 dirsType.tp_dealloc = (destructor)dirs_dealloc;
309 dirsType.tp_dealloc = (destructor)dirs_dealloc;
314 dirsType.tp_as_sequence = &dirs_sequence_methods;
310 dirsType.tp_as_sequence = &dirs_sequence_methods;
315 dirsType.tp_flags = Py_TPFLAGS_DEFAULT;
311 dirsType.tp_flags = Py_TPFLAGS_DEFAULT;
316 dirsType.tp_doc = "dirs";
312 dirsType.tp_doc = "dirs";
317 dirsType.tp_iter = (getiterfunc)dirs_iter;
313 dirsType.tp_iter = (getiterfunc)dirs_iter;
318 dirsType.tp_methods = dirs_methods;
314 dirsType.tp_methods = dirs_methods;
319 dirsType.tp_init = (initproc)dirs_init;
315 dirsType.tp_init = (initproc)dirs_init;
320
316
321 if (PyType_Ready(&dirsType) < 0)
317 if (PyType_Ready(&dirsType) < 0)
322 return;
318 return;
323 Py_INCREF(&dirsType);
319 Py_INCREF(&dirsType);
324
320
325 PyModule_AddObject(mod, "dirs", (PyObject *)&dirsType);
321 PyModule_AddObject(mod, "dirs", (PyObject *)&dirsType);
326 }
322 }
General Comments 0
You need to be logged in to leave comments. Login now