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