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