Mercurial > hg
comparison mercurial/cext/dirs.c @ 32372:df448de7cf3b
parsers: switch to policy importer
# no-check-commit
author | Yuya Nishihara <yuya@tcha.org> |
---|---|
date | Sat, 13 Aug 2016 12:23:56 +0900 |
parents | mercurial/dirs.c@1e5ff5ae1d2b |
children | b90e8da190da |
comparison
equal
deleted
inserted
replaced
32371:151cc3b3d799 | 32372:df448de7cf3b |
---|---|
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 #ifdef IS_PY3K | |
15 #define PYLONG_VALUE(o) ((PyLongObject *)o)->ob_digit[1] | |
16 #else | |
17 #define PYLONG_VALUE(o) PyInt_AS_LONG(o) | |
18 #endif | |
19 | |
20 /* | |
21 * This is a multiset of directory names, built from the files that | |
22 * appear in a dirstate or manifest. | |
23 * | |
24 * A few implementation notes: | |
25 * | |
26 * We modify Python integers for refcounting, but those integers are | |
27 * never visible to Python code. | |
28 * | |
29 * We mutate strings in-place, but leave them immutable once they can | |
30 * be seen by Python code. | |
31 */ | |
32 typedef struct { | |
33 PyObject_HEAD | |
34 PyObject *dict; | |
35 } dirsObject; | |
36 | |
37 static inline Py_ssize_t _finddir(const char *path, Py_ssize_t pos) | |
38 { | |
39 while (pos != -1) { | |
40 if (path[pos] == '/') | |
41 break; | |
42 pos -= 1; | |
43 } | |
44 | |
45 return pos; | |
46 } | |
47 | |
48 static int _addpath(PyObject *dirs, PyObject *path) | |
49 { | |
50 const char *cpath = PyBytes_AS_STRING(path); | |
51 Py_ssize_t pos = PyBytes_GET_SIZE(path); | |
52 PyObject *key = NULL; | |
53 int ret = -1; | |
54 | |
55 /* This loop is super critical for performance. That's why we inline | |
56 * access to Python structs instead of going through a supported API. | |
57 * The implementation, therefore, is heavily dependent on CPython | |
58 * implementation details. We also commit violations of the Python | |
59 * "protocol" such as mutating immutable objects. But since we only | |
60 * mutate objects created in this function or in other well-defined | |
61 * locations, the references are known so these violations should go | |
62 * unnoticed. The code for adjusting the length of a PyBytesObject is | |
63 * essentially a minimal version of _PyBytes_Resize. */ | |
64 while ((pos = _finddir(cpath, pos - 1)) != -1) { | |
65 PyObject *val; | |
66 | |
67 /* It's likely that every prefix already has an entry | |
68 in our dict. Try to avoid allocating and | |
69 deallocating a string for each prefix we check. */ | |
70 if (key != NULL) | |
71 ((PyBytesObject *)key)->ob_shash = -1; | |
72 else { | |
73 /* Force Python to not reuse a small shared string. */ | |
74 key = PyBytes_FromStringAndSize(cpath, | |
75 pos < 2 ? 2 : pos); | |
76 if (key == NULL) | |
77 goto bail; | |
78 } | |
79 /* Py_SIZE(o) refers to the ob_size member of the struct. Yes, | |
80 * assigning to what looks like a function seems wrong. */ | |
81 Py_SIZE(key) = pos; | |
82 ((PyBytesObject *)key)->ob_sval[pos] = '\0'; | |
83 | |
84 val = PyDict_GetItem(dirs, key); | |
85 if (val != NULL) { | |
86 PYLONG_VALUE(val) += 1; | |
87 break; | |
88 } | |
89 | |
90 /* Force Python to not reuse a small shared int. */ | |
91 #ifdef IS_PY3K | |
92 val = PyLong_FromLong(0x1eadbeef); | |
93 #else | |
94 val = PyInt_FromLong(0x1eadbeef); | |
95 #endif | |
96 | |
97 if (val == NULL) | |
98 goto bail; | |
99 | |
100 PYLONG_VALUE(val) = 1; | |
101 ret = PyDict_SetItem(dirs, key, val); | |
102 Py_DECREF(val); | |
103 if (ret == -1) | |
104 goto bail; | |
105 Py_CLEAR(key); | |
106 } | |
107 ret = 0; | |
108 | |
109 bail: | |
110 Py_XDECREF(key); | |
111 | |
112 return ret; | |
113 } | |
114 | |
115 static int _delpath(PyObject *dirs, PyObject *path) | |
116 { | |
117 char *cpath = PyBytes_AS_STRING(path); | |
118 Py_ssize_t pos = PyBytes_GET_SIZE(path); | |
119 PyObject *key = NULL; | |
120 int ret = -1; | |
121 | |
122 while ((pos = _finddir(cpath, pos - 1)) != -1) { | |
123 PyObject *val; | |
124 | |
125 key = PyBytes_FromStringAndSize(cpath, pos); | |
126 | |
127 if (key == NULL) | |
128 goto bail; | |
129 | |
130 val = PyDict_GetItem(dirs, key); | |
131 if (val == NULL) { | |
132 PyErr_SetString(PyExc_ValueError, | |
133 "expected a value, found none"); | |
134 goto bail; | |
135 } | |
136 | |
137 if (--PYLONG_VALUE(val) <= 0) { | |
138 if (PyDict_DelItem(dirs, key) == -1) | |
139 goto bail; | |
140 } else | |
141 break; | |
142 Py_CLEAR(key); | |
143 } | |
144 ret = 0; | |
145 | |
146 bail: | |
147 Py_XDECREF(key); | |
148 | |
149 return ret; | |
150 } | |
151 | |
152 static int dirs_fromdict(PyObject *dirs, PyObject *source, char skipchar) | |
153 { | |
154 PyObject *key, *value; | |
155 Py_ssize_t pos = 0; | |
156 | |
157 while (PyDict_Next(source, &pos, &key, &value)) { | |
158 if (!PyBytes_Check(key)) { | |
159 PyErr_SetString(PyExc_TypeError, "expected string key"); | |
160 return -1; | |
161 } | |
162 if (skipchar) { | |
163 if (!dirstate_tuple_check(value)) { | |
164 PyErr_SetString(PyExc_TypeError, | |
165 "expected a dirstate tuple"); | |
166 return -1; | |
167 } | |
168 if (((dirstateTupleObject *)value)->state == skipchar) | |
169 continue; | |
170 } | |
171 | |
172 if (_addpath(dirs, key) == -1) | |
173 return -1; | |
174 } | |
175 | |
176 return 0; | |
177 } | |
178 | |
179 static int dirs_fromiter(PyObject *dirs, PyObject *source) | |
180 { | |
181 PyObject *iter, *item = NULL; | |
182 int ret; | |
183 | |
184 iter = PyObject_GetIter(source); | |
185 if (iter == NULL) | |
186 return -1; | |
187 | |
188 while ((item = PyIter_Next(iter)) != NULL) { | |
189 if (!PyBytes_Check(item)) { | |
190 PyErr_SetString(PyExc_TypeError, "expected string"); | |
191 break; | |
192 } | |
193 | |
194 if (_addpath(dirs, item) == -1) | |
195 break; | |
196 Py_CLEAR(item); | |
197 } | |
198 | |
199 ret = PyErr_Occurred() ? -1 : 0; | |
200 Py_DECREF(iter); | |
201 Py_XDECREF(item); | |
202 return ret; | |
203 } | |
204 | |
205 /* | |
206 * Calculate a refcounted set of directory names for the files in a | |
207 * dirstate. | |
208 */ | |
209 static int dirs_init(dirsObject *self, PyObject *args) | |
210 { | |
211 PyObject *dirs = NULL, *source = NULL; | |
212 char skipchar = 0; | |
213 int ret = -1; | |
214 | |
215 self->dict = NULL; | |
216 | |
217 if (!PyArg_ParseTuple(args, "|Oc:__init__", &source, &skipchar)) | |
218 return -1; | |
219 | |
220 dirs = PyDict_New(); | |
221 | |
222 if (dirs == NULL) | |
223 return -1; | |
224 | |
225 if (source == NULL) | |
226 ret = 0; | |
227 else if (PyDict_Check(source)) | |
228 ret = dirs_fromdict(dirs, source, skipchar); | |
229 else if (skipchar) | |
230 PyErr_SetString(PyExc_ValueError, | |
231 "skip character is only supported " | |
232 "with a dict source"); | |
233 else | |
234 ret = dirs_fromiter(dirs, source); | |
235 | |
236 if (ret == -1) | |
237 Py_XDECREF(dirs); | |
238 else | |
239 self->dict = dirs; | |
240 | |
241 return ret; | |
242 } | |
243 | |
244 PyObject *dirs_addpath(dirsObject *self, PyObject *args) | |
245 { | |
246 PyObject *path; | |
247 | |
248 if (!PyArg_ParseTuple(args, "O!:addpath", &PyBytes_Type, &path)) | |
249 return NULL; | |
250 | |
251 if (_addpath(self->dict, path) == -1) | |
252 return NULL; | |
253 | |
254 Py_RETURN_NONE; | |
255 } | |
256 | |
257 static PyObject *dirs_delpath(dirsObject *self, PyObject *args) | |
258 { | |
259 PyObject *path; | |
260 | |
261 if (!PyArg_ParseTuple(args, "O!:delpath", &PyBytes_Type, &path)) | |
262 return NULL; | |
263 | |
264 if (_delpath(self->dict, path) == -1) | |
265 return NULL; | |
266 | |
267 Py_RETURN_NONE; | |
268 } | |
269 | |
270 static int dirs_contains(dirsObject *self, PyObject *value) | |
271 { | |
272 return PyBytes_Check(value) ? PyDict_Contains(self->dict, value) : 0; | |
273 } | |
274 | |
275 static void dirs_dealloc(dirsObject *self) | |
276 { | |
277 Py_XDECREF(self->dict); | |
278 PyObject_Del(self); | |
279 } | |
280 | |
281 static PyObject *dirs_iter(dirsObject *self) | |
282 { | |
283 return PyObject_GetIter(self->dict); | |
284 } | |
285 | |
286 static PySequenceMethods dirs_sequence_methods; | |
287 | |
288 static PyMethodDef dirs_methods[] = { | |
289 {"addpath", (PyCFunction)dirs_addpath, METH_VARARGS, "add a path"}, | |
290 {"delpath", (PyCFunction)dirs_delpath, METH_VARARGS, "remove a path"}, | |
291 {NULL} /* Sentinel */ | |
292 }; | |
293 | |
294 static PyTypeObject dirsType = { PyVarObject_HEAD_INIT(NULL, 0) }; | |
295 | |
296 void dirs_module_init(PyObject *mod) | |
297 { | |
298 dirs_sequence_methods.sq_contains = (objobjproc)dirs_contains; | |
299 dirsType.tp_name = "parsers.dirs"; | |
300 dirsType.tp_new = PyType_GenericNew; | |
301 dirsType.tp_basicsize = sizeof(dirsObject); | |
302 dirsType.tp_dealloc = (destructor)dirs_dealloc; | |
303 dirsType.tp_as_sequence = &dirs_sequence_methods; | |
304 dirsType.tp_flags = Py_TPFLAGS_DEFAULT; | |
305 dirsType.tp_doc = "dirs"; | |
306 dirsType.tp_iter = (getiterfunc)dirs_iter; | |
307 dirsType.tp_methods = dirs_methods; | |
308 dirsType.tp_init = (initproc)dirs_init; | |
309 | |
310 if (PyType_Ready(&dirsType) < 0) | |
311 return; | |
312 Py_INCREF(&dirsType); | |
313 | |
314 PyModule_AddObject(mod, "dirs", (PyObject *)&dirsType); | |
315 } |