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