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