##// END OF EJS Templates
dirs: speed up by storing number of direct children per dir...
Martin von Zweigbergk -
r25016:42e89b87 default
parent child Browse files
Show More
@@ -1,293 +1,295 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 break;
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 if (PyDict_DelItem(dirs, key) == -1)
119 goto bail;
119 goto bail;
120 } else
121 break;
120 Py_CLEAR(key);
122 Py_CLEAR(key);
121 }
123 }
122 ret = 0;
124 ret = 0;
123
125
124 bail:
126 bail:
125 Py_XDECREF(key);
127 Py_XDECREF(key);
126
128
127 return ret;
129 return ret;
128 }
130 }
129
131
130 static int dirs_fromdict(PyObject *dirs, PyObject *source, char skipchar)
132 static int dirs_fromdict(PyObject *dirs, PyObject *source, char skipchar)
131 {
133 {
132 PyObject *key, *value;
134 PyObject *key, *value;
133 Py_ssize_t pos = 0;
135 Py_ssize_t pos = 0;
134
136
135 while (PyDict_Next(source, &pos, &key, &value)) {
137 while (PyDict_Next(source, &pos, &key, &value)) {
136 if (!PyString_Check(key)) {
138 if (!PyString_Check(key)) {
137 PyErr_SetString(PyExc_TypeError, "expected string key");
139 PyErr_SetString(PyExc_TypeError, "expected string key");
138 return -1;
140 return -1;
139 }
141 }
140 if (skipchar) {
142 if (skipchar) {
141 if (!dirstate_tuple_check(value)) {
143 if (!dirstate_tuple_check(value)) {
142 PyErr_SetString(PyExc_TypeError,
144 PyErr_SetString(PyExc_TypeError,
143 "expected a dirstate tuple");
145 "expected a dirstate tuple");
144 return -1;
146 return -1;
145 }
147 }
146 if (((dirstateTupleObject *)value)->state == skipchar)
148 if (((dirstateTupleObject *)value)->state == skipchar)
147 continue;
149 continue;
148 }
150 }
149
151
150 if (_addpath(dirs, key) == -1)
152 if (_addpath(dirs, key) == -1)
151 return -1;
153 return -1;
152 }
154 }
153
155
154 return 0;
156 return 0;
155 }
157 }
156
158
157 static int dirs_fromiter(PyObject *dirs, PyObject *source)
159 static int dirs_fromiter(PyObject *dirs, PyObject *source)
158 {
160 {
159 PyObject *iter, *item = NULL;
161 PyObject *iter, *item = NULL;
160 int ret;
162 int ret;
161
163
162 iter = PyObject_GetIter(source);
164 iter = PyObject_GetIter(source);
163 if (iter == NULL)
165 if (iter == NULL)
164 return -1;
166 return -1;
165
167
166 while ((item = PyIter_Next(iter)) != NULL) {
168 while ((item = PyIter_Next(iter)) != NULL) {
167 if (!PyString_Check(item)) {
169 if (!PyString_Check(item)) {
168 PyErr_SetString(PyExc_TypeError, "expected string");
170 PyErr_SetString(PyExc_TypeError, "expected string");
169 break;
171 break;
170 }
172 }
171
173
172 if (_addpath(dirs, item) == -1)
174 if (_addpath(dirs, item) == -1)
173 break;
175 break;
174 Py_CLEAR(item);
176 Py_CLEAR(item);
175 }
177 }
176
178
177 ret = PyErr_Occurred() ? -1 : 0;
179 ret = PyErr_Occurred() ? -1 : 0;
178 Py_DECREF(iter);
180 Py_DECREF(iter);
179 Py_XDECREF(item);
181 Py_XDECREF(item);
180 return ret;
182 return ret;
181 }
183 }
182
184
183 /*
185 /*
184 * Calculate a refcounted set of directory names for the files in a
186 * Calculate a refcounted set of directory names for the files in a
185 * dirstate.
187 * dirstate.
186 */
188 */
187 static int dirs_init(dirsObject *self, PyObject *args)
189 static int dirs_init(dirsObject *self, PyObject *args)
188 {
190 {
189 PyObject *dirs = NULL, *source = NULL;
191 PyObject *dirs = NULL, *source = NULL;
190 char skipchar = 0;
192 char skipchar = 0;
191 int ret = -1;
193 int ret = -1;
192
194
193 self->dict = NULL;
195 self->dict = NULL;
194
196
195 if (!PyArg_ParseTuple(args, "|Oc:__init__", &source, &skipchar))
197 if (!PyArg_ParseTuple(args, "|Oc:__init__", &source, &skipchar))
196 return -1;
198 return -1;
197
199
198 dirs = PyDict_New();
200 dirs = PyDict_New();
199
201
200 if (dirs == NULL)
202 if (dirs == NULL)
201 return -1;
203 return -1;
202
204
203 if (source == NULL)
205 if (source == NULL)
204 ret = 0;
206 ret = 0;
205 else if (PyDict_Check(source))
207 else if (PyDict_Check(source))
206 ret = dirs_fromdict(dirs, source, skipchar);
208 ret = dirs_fromdict(dirs, source, skipchar);
207 else if (skipchar)
209 else if (skipchar)
208 PyErr_SetString(PyExc_ValueError,
210 PyErr_SetString(PyExc_ValueError,
209 "skip character is only supported "
211 "skip character is only supported "
210 "with a dict source");
212 "with a dict source");
211 else
213 else
212 ret = dirs_fromiter(dirs, source);
214 ret = dirs_fromiter(dirs, source);
213
215
214 if (ret == -1)
216 if (ret == -1)
215 Py_XDECREF(dirs);
217 Py_XDECREF(dirs);
216 else
218 else
217 self->dict = dirs;
219 self->dict = dirs;
218
220
219 return ret;
221 return ret;
220 }
222 }
221
223
222 PyObject *dirs_addpath(dirsObject *self, PyObject *args)
224 PyObject *dirs_addpath(dirsObject *self, PyObject *args)
223 {
225 {
224 PyObject *path;
226 PyObject *path;
225
227
226 if (!PyArg_ParseTuple(args, "O!:addpath", &PyString_Type, &path))
228 if (!PyArg_ParseTuple(args, "O!:addpath", &PyString_Type, &path))
227 return NULL;
229 return NULL;
228
230
229 if (_addpath(self->dict, path) == -1)
231 if (_addpath(self->dict, path) == -1)
230 return NULL;
232 return NULL;
231
233
232 Py_RETURN_NONE;
234 Py_RETURN_NONE;
233 }
235 }
234
236
235 static PyObject *dirs_delpath(dirsObject *self, PyObject *args)
237 static PyObject *dirs_delpath(dirsObject *self, PyObject *args)
236 {
238 {
237 PyObject *path;
239 PyObject *path;
238
240
239 if (!PyArg_ParseTuple(args, "O!:delpath", &PyString_Type, &path))
241 if (!PyArg_ParseTuple(args, "O!:delpath", &PyString_Type, &path))
240 return NULL;
242 return NULL;
241
243
242 if (_delpath(self->dict, path) == -1)
244 if (_delpath(self->dict, path) == -1)
243 return NULL;
245 return NULL;
244
246
245 Py_RETURN_NONE;
247 Py_RETURN_NONE;
246 }
248 }
247
249
248 static int dirs_contains(dirsObject *self, PyObject *value)
250 static int dirs_contains(dirsObject *self, PyObject *value)
249 {
251 {
250 return PyString_Check(value) ? PyDict_Contains(self->dict, value) : 0;
252 return PyString_Check(value) ? PyDict_Contains(self->dict, value) : 0;
251 }
253 }
252
254
253 static void dirs_dealloc(dirsObject *self)
255 static void dirs_dealloc(dirsObject *self)
254 {
256 {
255 Py_XDECREF(self->dict);
257 Py_XDECREF(self->dict);
256 PyObject_Del(self);
258 PyObject_Del(self);
257 }
259 }
258
260
259 static PyObject *dirs_iter(dirsObject *self)
261 static PyObject *dirs_iter(dirsObject *self)
260 {
262 {
261 return PyObject_GetIter(self->dict);
263 return PyObject_GetIter(self->dict);
262 }
264 }
263
265
264 static PySequenceMethods dirs_sequence_methods;
266 static PySequenceMethods dirs_sequence_methods;
265
267
266 static PyMethodDef dirs_methods[] = {
268 static PyMethodDef dirs_methods[] = {
267 {"addpath", (PyCFunction)dirs_addpath, METH_VARARGS, "add a path"},
269 {"addpath", (PyCFunction)dirs_addpath, METH_VARARGS, "add a path"},
268 {"delpath", (PyCFunction)dirs_delpath, METH_VARARGS, "remove a path"},
270 {"delpath", (PyCFunction)dirs_delpath, METH_VARARGS, "remove a path"},
269 {NULL} /* Sentinel */
271 {NULL} /* Sentinel */
270 };
272 };
271
273
272 static PyTypeObject dirsType = { PyObject_HEAD_INIT(NULL) };
274 static PyTypeObject dirsType = { PyObject_HEAD_INIT(NULL) };
273
275
274 void dirs_module_init(PyObject *mod)
276 void dirs_module_init(PyObject *mod)
275 {
277 {
276 dirs_sequence_methods.sq_contains = (objobjproc)dirs_contains;
278 dirs_sequence_methods.sq_contains = (objobjproc)dirs_contains;
277 dirsType.tp_name = "parsers.dirs";
279 dirsType.tp_name = "parsers.dirs";
278 dirsType.tp_new = PyType_GenericNew;
280 dirsType.tp_new = PyType_GenericNew;
279 dirsType.tp_basicsize = sizeof(dirsObject);
281 dirsType.tp_basicsize = sizeof(dirsObject);
280 dirsType.tp_dealloc = (destructor)dirs_dealloc;
282 dirsType.tp_dealloc = (destructor)dirs_dealloc;
281 dirsType.tp_as_sequence = &dirs_sequence_methods;
283 dirsType.tp_as_sequence = &dirs_sequence_methods;
282 dirsType.tp_flags = Py_TPFLAGS_DEFAULT;
284 dirsType.tp_flags = Py_TPFLAGS_DEFAULT;
283 dirsType.tp_doc = "dirs";
285 dirsType.tp_doc = "dirs";
284 dirsType.tp_iter = (getiterfunc)dirs_iter;
286 dirsType.tp_iter = (getiterfunc)dirs_iter;
285 dirsType.tp_methods = dirs_methods;
287 dirsType.tp_methods = dirs_methods;
286 dirsType.tp_init = (initproc)dirs_init;
288 dirsType.tp_init = (initproc)dirs_init;
287
289
288 if (PyType_Ready(&dirsType) < 0)
290 if (PyType_Ready(&dirsType) < 0)
289 return;
291 return;
290 Py_INCREF(&dirsType);
292 Py_INCREF(&dirsType);
291
293
292 PyModule_AddObject(mod, "dirs", (PyObject *)&dirsType);
294 PyModule_AddObject(mod, "dirs", (PyObject *)&dirsType);
293 }
295 }
General Comments 0
You need to be logged in to leave comments. Login now