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