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