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