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