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