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