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