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