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