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