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