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