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