##// END OF EJS Templates
scmutil: rewrite dirs in C, use if available...
Bryan O'Sullivan -
r18900:02ee846b default
parent child Browse files
Show More
@@ -0,0 +1,298 b''
1 /*
2 dirs.c - dynamic directory diddling for dirstates
3
4 Copyright 2013 Facebook
5
6 This software may be used and distributed according to the terms of
7 the GNU General Public License, incorporated herein by reference.
8 */
9
10 #define PY_SSIZE_T_CLEAN
11 #include <Python.h>
12 #include "util.h"
13
14 /*
15 * This is a multiset of directory names, built from the files that
16 * appear in a dirstate or manifest.
17 */
18 typedef struct {
19 PyObject_HEAD
20 PyObject *dict;
21 } dirsObject;
22
23 static inline Py_ssize_t _finddir(PyObject *path, Py_ssize_t pos)
24 {
25 const char *s = PyString_AS_STRING(path);
26
27 while (pos != -1) {
28 if (s[pos] == '/')
29 break;
30 pos -= 1;
31 }
32
33 return pos;
34 }
35
36 static int _addpath(PyObject *dirs, PyObject *path)
37 {
38 Py_ssize_t pos = PyString_GET_SIZE(path);
39 PyObject *newval = NULL, *key = NULL;
40 int ret = -1;
41
42 while ((pos = _finddir(path, pos - 1)) != -1) {
43 PyObject *val;
44 long v = 0;
45
46 key = PyString_FromStringAndSize(PyString_AS_STRING(path), pos);
47
48 if (key == NULL)
49 goto bail;
50
51 val = PyDict_GetItem(dirs, key);
52 if (val != NULL)
53 v = PyInt_AS_LONG(val);
54
55 newval = PyInt_FromLong(v + 1);
56
57 if (newval == NULL)
58 goto bail;
59
60 ret = PyDict_SetItem(dirs, key, newval);
61 if (ret == -1)
62 goto bail;
63 Py_CLEAR(key);
64 Py_CLEAR(newval);
65 }
66 ret = 0;
67
68 bail:
69 Py_XDECREF(key);
70 Py_XDECREF(newval);
71
72 return ret;
73 }
74
75 static int _delpath(PyObject *dirs, PyObject *path)
76 {
77 Py_ssize_t pos = PyString_GET_SIZE(path);
78 PyObject *newval = NULL, *key = NULL;
79 int ret = -1;
80
81 while ((pos = _finddir(path, pos - 1)) != -1) {
82 PyObject *val;
83 long v;
84
85 key = PyString_FromStringAndSize(PyString_AS_STRING(path), pos);
86
87 if (key == NULL)
88 goto bail;
89
90 val = PyDict_GetItem(dirs, key);
91 if (val == NULL) {
92 PyErr_SetString(PyExc_ValueError,
93 "expected a value, found none");
94 goto bail;
95 }
96 v = PyInt_AS_LONG(val);
97
98 if (v <= 1) {
99 if (PyDict_DelItem(dirs, key) == -1)
100 goto bail;
101 continue;
102 }
103 newval = PyInt_FromLong(v - 1);
104
105 if (newval == NULL)
106 goto bail;
107
108 ret = PyDict_SetItem(dirs, key, newval);
109 if (ret == -1)
110 goto bail;
111 Py_CLEAR(key);
112 Py_CLEAR(newval);
113 }
114 ret = 0;
115
116 bail:
117 Py_XDECREF(key);
118 Py_XDECREF(newval);
119
120 return ret;
121 }
122
123 static int dirs_fromdict(PyObject *dirs, PyObject *source, char skipchar)
124 {
125 PyObject *key, *value;
126 Py_ssize_t pos = 0;
127
128 while (PyDict_Next(source, &pos, &key, &value)) {
129 if (!PyString_Check(key)) {
130 PyErr_SetString(PyExc_TypeError, "expected string key");
131 return -1;
132 }
133 if (skipchar) {
134 PyObject *st;
135
136 if (!PyTuple_Check(value) ||
137 PyTuple_GET_SIZE(value) == 0) {
138 PyErr_SetString(PyExc_TypeError,
139 "expected non-empty tuple");
140 return -1;
141 }
142
143 st = PyTuple_GET_ITEM(value, 0);
144
145 if (!PyString_Check(st) || PyString_GET_SIZE(st) == 0) {
146 PyErr_SetString(PyExc_TypeError,
147 "expected non-empty string "
148 "at tuple index 0");
149 return -1;
150 }
151
152 if (PyString_AS_STRING(st)[0] == skipchar)
153 continue;
154 }
155
156 if (_addpath(dirs, key) == -1)
157 return -1;
158 }
159
160 return 0;
161 }
162
163 static int dirs_fromiter(PyObject *dirs, PyObject *source)
164 {
165 PyObject *iter, *item = NULL;
166 int ret;
167
168 iter = PyObject_GetIter(source);
169 if (iter == NULL)
170 return -1;
171
172 while ((item = PyIter_Next(iter)) != NULL) {
173 if (!PyString_Check(item)) {
174 PyErr_SetString(PyExc_TypeError, "expected string");
175 break;
176 }
177
178 if (_addpath(dirs, item) == -1)
179 break;
180 Py_CLEAR(item);
181 }
182
183 ret = PyErr_Occurred() ? -1 : 0;
184 Py_XDECREF(item);
185 return ret;
186 }
187
188 /*
189 * Calculate a refcounted set of directory names for the files in a
190 * dirstate.
191 */
192 static int dirs_init(dirsObject *self, PyObject *args)
193 {
194 PyObject *dirs = NULL, *source = NULL;
195 char skipchar = 0;
196 int ret = -1;
197
198 self->dict = NULL;
199
200 if (!PyArg_ParseTuple(args, "|Oc:__init__", &source, &skipchar))
201 return -1;
202
203 dirs = PyDict_New();
204
205 if (dirs == NULL)
206 return -1;
207
208 if (source == NULL)
209 ret = 0;
210 else if (PyDict_Check(source))
211 ret = dirs_fromdict(dirs, source, skipchar);
212 else if (skipchar)
213 PyErr_SetString(PyExc_ValueError,
214 "skip character is only supported "
215 "with a dict source");
216 else
217 ret = dirs_fromiter(dirs, source);
218
219 if (ret == -1)
220 Py_XDECREF(dirs);
221 else
222 self->dict = dirs;
223
224 return ret;
225 }
226
227 PyObject *dirs_addpath(dirsObject *self, PyObject *args)
228 {
229 PyObject *path;
230
231 if (!PyArg_ParseTuple(args, "O!:addpath", &PyString_Type, &path))
232 return NULL;
233
234 if (_addpath(self->dict, path) == -1)
235 return NULL;
236
237 Py_RETURN_NONE;
238 }
239
240 static PyObject *dirs_delpath(dirsObject *self, PyObject *args)
241 {
242 PyObject *path;
243
244 if (!PyArg_ParseTuple(args, "O!:delpath", &PyString_Type, &path))
245 return NULL;
246
247 if (_delpath(self->dict, path) == -1)
248 return NULL;
249
250 Py_RETURN_NONE;
251 }
252
253 static int dirs_contains(dirsObject *self, PyObject *value)
254 {
255 return PyString_Check(value) ? PyDict_Contains(self->dict, value) : 0;
256 }
257
258 static void dirs_dealloc(dirsObject *self)
259 {
260 Py_XDECREF(self->dict);
261 PyObject_Del(self);
262 }
263
264 static PyObject *dirs_iter(dirsObject *self)
265 {
266 return PyObject_GetIter(self->dict);
267 }
268
269 static PySequenceMethods dirs_sequence_methods;
270
271 static PyMethodDef dirs_methods[] = {
272 {"addpath", (PyCFunction)dirs_addpath, METH_VARARGS, "add a path"},
273 {"delpath", (PyCFunction)dirs_delpath, METH_VARARGS, "remove a path"},
274 {NULL} /* Sentinel */
275 };
276
277 static PyTypeObject dirsType = { PyObject_HEAD_INIT(NULL) };
278
279 void dirs_module_init(PyObject *mod)
280 {
281 dirs_sequence_methods.sq_contains = (objobjproc)dirs_contains;
282 dirsType.tp_name = "parsers.dirs";
283 dirsType.tp_new = PyType_GenericNew;
284 dirsType.tp_basicsize = sizeof(dirsObject);
285 dirsType.tp_dealloc = (destructor)dirs_dealloc;
286 dirsType.tp_as_sequence = &dirs_sequence_methods;
287 dirsType.tp_flags = Py_TPFLAGS_DEFAULT;
288 dirsType.tp_doc = "dirs";
289 dirsType.tp_iter = (getiterfunc)dirs_iter;
290 dirsType.tp_methods = dirs_methods;
291 dirsType.tp_init = (initproc)dirs_init;
292
293 if (PyType_Ready(&dirsType) < 0)
294 return;
295 Py_INCREF(&dirsType);
296
297 PyModule_AddObject(mod, "dirs", (PyObject *)&dirsType);
298 }
@@ -1528,8 +1528,12 b' static PyMethodDef methods[] = {'
1528 1528 {NULL, NULL}
1529 1529 };
1530 1530
1531 void dirs_module_init(PyObject *mod);
1532
1531 1533 static void module_init(PyObject *mod)
1532 1534 {
1535 dirs_module_init(mod);
1536
1533 1537 indexType.tp_new = PyType_GenericNew;
1534 1538 if (PyType_Ready(&indexType) < 0)
1535 1539 return;
@@ -7,7 +7,7 b''
7 7
8 8 from i18n import _
9 9 from mercurial.node import nullrev
10 import util, error, osutil, revset, similar, encoding, phases
10 import util, error, osutil, revset, similar, encoding, phases, parsers
11 11 import match as matchmod
12 12 import os, errno, re, stat, glob
13 13
@@ -927,6 +927,9 b' class dirs(object):'
927 927 def __contains__(self, d):
928 928 return d in self._dirs
929 929
930 if util.safehasattr(parsers, 'dirs'):
931 dirs = parsers.dirs
932
930 933 def finddirs(path):
931 934 pos = path.rfind('/')
932 935 while pos != -1:
@@ -427,7 +427,8 b' extmodules = ['
427 427 Extension('mercurial.bdiff', ['mercurial/bdiff.c']),
428 428 Extension('mercurial.diffhelpers', ['mercurial/diffhelpers.c']),
429 429 Extension('mercurial.mpatch', ['mercurial/mpatch.c']),
430 Extension('mercurial.parsers', ['mercurial/parsers.c',
430 Extension('mercurial.parsers', ['mercurial/dirs.c',
431 'mercurial/parsers.c',
431 432 'mercurial/pathencode.c']),
432 433 ]
433 434
General Comments 0
You need to be logged in to leave comments. Login now