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