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