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