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