Line data Source code
1 : /* Implementation helper: a struct that looks like a tuple. See timemodule
2 : and posixmodule for example uses. */
3 :
4 : #include "Python.h"
5 : #include "structmember.h"
6 : #include "structseq.h"
7 :
8 : static char visible_length_key[] = "n_sequence_fields";
9 : static char real_length_key[] = "n_fields";
10 : static char unnamed_fields_key[] = "n_unnamed_fields";
11 :
12 : /* Fields with this name have only a field index, not a field name.
13 : They are only allowed for indices < n_visible_fields. */
14 : char *PyStructSequence_UnnamedField = "unnamed field";
15 :
16 : #define VISIBLE_SIZE(op) Py_SIZE(op)
17 : #define VISIBLE_SIZE_TP(tp) PyInt_AsLong( \
18 : PyDict_GetItemString((tp)->tp_dict, visible_length_key))
19 :
20 : #define REAL_SIZE_TP(tp) PyInt_AsLong( \
21 : PyDict_GetItemString((tp)->tp_dict, real_length_key))
22 : #define REAL_SIZE(op) REAL_SIZE_TP(Py_TYPE(op))
23 :
24 : #define UNNAMED_FIELDS_TP(tp) PyInt_AsLong( \
25 : PyDict_GetItemString((tp)->tp_dict, unnamed_fields_key))
26 : #define UNNAMED_FIELDS(op) UNNAMED_FIELDS_TP(Py_TYPE(op))
27 :
28 :
29 : PyObject *
30 12 : PyStructSequence_New(PyTypeObject *type)
31 : {
32 : PyStructSequence *obj;
33 :
34 12 : obj = PyObject_New(PyStructSequence, type);
35 12 : if (obj == NULL)
36 0 : return NULL;
37 12 : Py_SIZE(obj) = VISIBLE_SIZE_TP(type);
38 :
39 12 : return (PyObject*) obj;
40 : }
41 :
42 : static void
43 12 : structseq_dealloc(PyStructSequence *obj)
44 : {
45 : Py_ssize_t i, size;
46 :
47 12 : size = REAL_SIZE(obj);
48 114 : for (i = 0; i < size; ++i) {
49 102 : Py_XDECREF(obj->ob_item[i]);
50 : }
51 12 : PyObject_Del(obj);
52 12 : }
53 :
54 : static Py_ssize_t
55 0 : structseq_length(PyStructSequence *obj)
56 : {
57 0 : return VISIBLE_SIZE(obj);
58 : }
59 :
60 : static PyObject*
61 0 : structseq_item(PyStructSequence *obj, Py_ssize_t i)
62 : {
63 0 : if (i < 0 || i >= VISIBLE_SIZE(obj)) {
64 0 : PyErr_SetString(PyExc_IndexError, "tuple index out of range");
65 0 : return NULL;
66 : }
67 0 : Py_INCREF(obj->ob_item[i]);
68 0 : return obj->ob_item[i];
69 : }
70 :
71 : static PyObject*
72 0 : structseq_slice(PyStructSequence *obj, Py_ssize_t low, Py_ssize_t high)
73 : {
74 : PyTupleObject *np;
75 : Py_ssize_t i;
76 :
77 0 : if (low < 0)
78 0 : low = 0;
79 0 : if (high > VISIBLE_SIZE(obj))
80 0 : high = VISIBLE_SIZE(obj);
81 0 : if (high < low)
82 0 : high = low;
83 0 : np = (PyTupleObject *)PyTuple_New(high-low);
84 0 : if (np == NULL)
85 0 : return NULL;
86 0 : for(i = low; i < high; ++i) {
87 0 : PyObject *v = obj->ob_item[i];
88 0 : Py_INCREF(v);
89 0 : PyTuple_SET_ITEM(np, i-low, v);
90 : }
91 0 : return (PyObject *) np;
92 : }
93 :
94 : static PyObject *
95 0 : structseq_subscript(PyStructSequence *self, PyObject *item)
96 : {
97 0 : if (PyIndex_Check(item)) {
98 0 : Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
99 0 : if (i == -1 && PyErr_Occurred())
100 0 : return NULL;
101 :
102 0 : if (i < 0)
103 0 : i += VISIBLE_SIZE(self);
104 :
105 0 : if (i < 0 || i >= VISIBLE_SIZE(self)) {
106 0 : PyErr_SetString(PyExc_IndexError,
107 : "tuple index out of range");
108 0 : return NULL;
109 : }
110 0 : Py_INCREF(self->ob_item[i]);
111 0 : return self->ob_item[i];
112 : }
113 0 : else if (PySlice_Check(item)) {
114 : Py_ssize_t start, stop, step, slicelen, cur, i;
115 : PyObject *result;
116 :
117 0 : if (PySlice_GetIndicesEx((PySliceObject *)item,
118 : VISIBLE_SIZE(self), &start, &stop,
119 : &step, &slicelen) < 0) {
120 0 : return NULL;
121 : }
122 0 : if (slicelen <= 0)
123 0 : return PyTuple_New(0);
124 0 : result = PyTuple_New(slicelen);
125 0 : if (result == NULL)
126 0 : return NULL;
127 0 : for (cur = start, i = 0; i < slicelen;
128 0 : cur += step, i++) {
129 0 : PyObject *v = self->ob_item[cur];
130 0 : Py_INCREF(v);
131 0 : PyTuple_SET_ITEM(result, i, v);
132 : }
133 0 : return result;
134 : }
135 : else {
136 0 : PyErr_SetString(PyExc_TypeError,
137 : "structseq index must be integer");
138 0 : return NULL;
139 : }
140 : }
141 :
142 : static PyObject *
143 0 : structseq_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
144 : {
145 0 : PyObject *arg = NULL;
146 0 : PyObject *dict = NULL;
147 : PyObject *ob;
148 0 : PyStructSequence *res = NULL;
149 : Py_ssize_t len, min_len, max_len, i, n_unnamed_fields;
150 : static char *kwlist[] = {"sequence", "dict", 0};
151 :
152 0 : if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:structseq",
153 : kwlist, &arg, &dict))
154 0 : return NULL;
155 :
156 0 : arg = PySequence_Fast(arg, "constructor requires a sequence");
157 :
158 0 : if (!arg) {
159 0 : return NULL;
160 : }
161 :
162 0 : if (dict && !PyDict_Check(dict)) {
163 0 : PyErr_Format(PyExc_TypeError,
164 : "%.500s() takes a dict as second arg, if any",
165 : type->tp_name);
166 0 : Py_DECREF(arg);
167 0 : return NULL;
168 : }
169 :
170 0 : len = PySequence_Fast_GET_SIZE(arg);
171 0 : min_len = VISIBLE_SIZE_TP(type);
172 0 : max_len = REAL_SIZE_TP(type);
173 0 : n_unnamed_fields = UNNAMED_FIELDS_TP(type);
174 :
175 0 : if (min_len != max_len) {
176 0 : if (len < min_len) {
177 0 : PyErr_Format(PyExc_TypeError,
178 : "%.500s() takes an at least %zd-sequence (%zd-sequence given)",
179 : type->tp_name, min_len, len);
180 0 : Py_DECREF(arg);
181 0 : return NULL;
182 : }
183 :
184 0 : if (len > max_len) {
185 0 : PyErr_Format(PyExc_TypeError,
186 : "%.500s() takes an at most %zd-sequence (%zd-sequence given)",
187 : type->tp_name, max_len, len);
188 0 : Py_DECREF(arg);
189 0 : return NULL;
190 : }
191 : }
192 : else {
193 0 : if (len != min_len) {
194 0 : PyErr_Format(PyExc_TypeError,
195 : "%.500s() takes a %zd-sequence (%zd-sequence given)",
196 : type->tp_name, min_len, len);
197 0 : Py_DECREF(arg);
198 0 : return NULL;
199 : }
200 : }
201 :
202 0 : res = (PyStructSequence*) PyStructSequence_New(type);
203 0 : if (res == NULL) {
204 0 : Py_DECREF(arg);
205 0 : return NULL;
206 : }
207 0 : for (i = 0; i < len; ++i) {
208 0 : PyObject *v = PySequence_Fast_GET_ITEM(arg, i);
209 0 : Py_INCREF(v);
210 0 : res->ob_item[i] = v;
211 : }
212 0 : for (; i < max_len; ++i) {
213 0 : if (dict && (ob = PyDict_GetItemString(
214 0 : dict, type->tp_members[i-n_unnamed_fields].name))) {
215 : }
216 : else {
217 0 : ob = Py_None;
218 : }
219 0 : Py_INCREF(ob);
220 0 : res->ob_item[i] = ob;
221 : }
222 :
223 0 : Py_DECREF(arg);
224 0 : return (PyObject*) res;
225 : }
226 :
227 : static PyObject *
228 0 : make_tuple(PyStructSequence *obj)
229 : {
230 0 : return structseq_slice(obj, 0, VISIBLE_SIZE(obj));
231 : }
232 :
233 : static PyObject *
234 0 : structseq_repr(PyStructSequence *obj)
235 : {
236 : /* buffer and type size were chosen well considered. */
237 : #define REPR_BUFFER_SIZE 512
238 : #define TYPE_MAXSIZE 100
239 :
240 : PyObject *tup;
241 0 : PyTypeObject *typ = Py_TYPE(obj);
242 0 : int i, removelast = 0;
243 : Py_ssize_t len;
244 : char buf[REPR_BUFFER_SIZE];
245 0 : char *endofbuf, *pbuf = buf;
246 :
247 : /* pointer to end of writeable buffer; safes space for "...)\0" */
248 0 : endofbuf= &buf[REPR_BUFFER_SIZE-5];
249 :
250 0 : if ((tup = make_tuple(obj)) == NULL) {
251 0 : return NULL;
252 : }
253 :
254 : /* "typename(", limited to TYPE_MAXSIZE */
255 0 : len = strlen(typ->tp_name) > TYPE_MAXSIZE ? TYPE_MAXSIZE :
256 0 : strlen(typ->tp_name);
257 0 : strncpy(pbuf, typ->tp_name, len);
258 0 : pbuf += len;
259 0 : *pbuf++ = '(';
260 :
261 0 : for (i=0; i < VISIBLE_SIZE(obj); i++) {
262 : PyObject *val, *repr;
263 : char *cname, *crepr;
264 :
265 0 : cname = typ->tp_members[i].name;
266 :
267 0 : val = PyTuple_GetItem(tup, i);
268 0 : if (cname == NULL || val == NULL) {
269 0 : return NULL;
270 : }
271 0 : repr = PyObject_Repr(val);
272 0 : if (repr == NULL) {
273 0 : Py_DECREF(tup);
274 0 : return NULL;
275 : }
276 0 : crepr = PyString_AsString(repr);
277 0 : if (crepr == NULL) {
278 0 : Py_DECREF(tup);
279 0 : Py_DECREF(repr);
280 0 : return NULL;
281 : }
282 :
283 : /* + 3: keep space for "=" and ", " */
284 0 : len = strlen(cname) + strlen(crepr) + 3;
285 0 : if ((pbuf+len) <= endofbuf) {
286 0 : strcpy(pbuf, cname);
287 0 : pbuf += strlen(cname);
288 0 : *pbuf++ = '=';
289 0 : strcpy(pbuf, crepr);
290 0 : pbuf += strlen(crepr);
291 0 : *pbuf++ = ',';
292 0 : *pbuf++ = ' ';
293 0 : removelast = 1;
294 0 : Py_DECREF(repr);
295 : }
296 : else {
297 0 : strcpy(pbuf, "...");
298 0 : pbuf += 3;
299 0 : removelast = 0;
300 0 : Py_DECREF(repr);
301 0 : break;
302 : }
303 : }
304 0 : Py_DECREF(tup);
305 0 : if (removelast) {
306 : /* overwrite last ", " */
307 0 : pbuf-=2;
308 : }
309 0 : *pbuf++ = ')';
310 0 : *pbuf = '\0';
311 :
312 0 : return PyString_FromString(buf);
313 : }
314 :
315 : static PyObject *
316 0 : structseq_concat(PyStructSequence *obj, PyObject *b)
317 : {
318 : PyObject *tup, *result;
319 0 : tup = make_tuple(obj);
320 0 : result = PySequence_Concat(tup, b);
321 0 : Py_DECREF(tup);
322 0 : return result;
323 : }
324 :
325 : static PyObject *
326 0 : structseq_repeat(PyStructSequence *obj, Py_ssize_t n)
327 : {
328 : PyObject *tup, *result;
329 0 : tup = make_tuple(obj);
330 0 : result = PySequence_Repeat(tup, n);
331 0 : Py_DECREF(tup);
332 0 : return result;
333 : }
334 :
335 : static int
336 0 : structseq_contains(PyStructSequence *obj, PyObject *o)
337 : {
338 : PyObject *tup;
339 : int result;
340 0 : tup = make_tuple(obj);
341 0 : if (!tup)
342 0 : return -1;
343 0 : result = PySequence_Contains(tup, o);
344 0 : Py_DECREF(tup);
345 0 : return result;
346 : }
347 :
348 : static long
349 0 : structseq_hash(PyObject *obj)
350 : {
351 : PyObject *tup;
352 : long result;
353 0 : tup = make_tuple((PyStructSequence*) obj);
354 0 : if (!tup)
355 0 : return -1;
356 0 : result = PyObject_Hash(tup);
357 0 : Py_DECREF(tup);
358 0 : return result;
359 : }
360 :
361 : static PyObject *
362 0 : structseq_richcompare(PyObject *obj, PyObject *o2, int op)
363 : {
364 : PyObject *tup, *result;
365 0 : tup = make_tuple((PyStructSequence*) obj);
366 0 : result = PyObject_RichCompare(tup, o2, op);
367 0 : Py_DECREF(tup);
368 0 : return result;
369 : }
370 :
371 : static PyObject *
372 0 : structseq_reduce(PyStructSequence* self)
373 : {
374 : PyObject* tup;
375 : PyObject* dict;
376 : PyObject* result;
377 : Py_ssize_t n_fields, n_visible_fields, n_unnamed_fields;
378 : int i;
379 :
380 0 : n_fields = REAL_SIZE(self);
381 0 : n_visible_fields = VISIBLE_SIZE(self);
382 0 : n_unnamed_fields = UNNAMED_FIELDS(self);
383 0 : tup = PyTuple_New(n_visible_fields);
384 0 : if (!tup) {
385 0 : return NULL;
386 : }
387 :
388 0 : dict = PyDict_New();
389 0 : if (!dict) {
390 0 : Py_DECREF(tup);
391 0 : return NULL;
392 : }
393 :
394 0 : for (i = 0; i < n_visible_fields; i++) {
395 0 : Py_INCREF(self->ob_item[i]);
396 0 : PyTuple_SET_ITEM(tup, i, self->ob_item[i]);
397 : }
398 :
399 0 : for (; i < n_fields; i++) {
400 0 : char *n = Py_TYPE(self)->tp_members[i-n_unnamed_fields].name;
401 0 : PyDict_SetItemString(dict, n,
402 : self->ob_item[i]);
403 : }
404 :
405 0 : result = Py_BuildValue("(O(OO))", Py_TYPE(self), tup, dict);
406 :
407 0 : Py_DECREF(tup);
408 0 : Py_DECREF(dict);
409 :
410 0 : return result;
411 : }
412 :
413 : static PySequenceMethods structseq_as_sequence = {
414 : (lenfunc)structseq_length,
415 : (binaryfunc)structseq_concat, /* sq_concat */
416 : (ssizeargfunc)structseq_repeat, /* sq_repeat */
417 : (ssizeargfunc)structseq_item, /* sq_item */
418 : (ssizessizeargfunc)structseq_slice, /* sq_slice */
419 : 0, /* sq_ass_item */
420 : 0, /* sq_ass_slice */
421 : (objobjproc)structseq_contains, /* sq_contains */
422 : };
423 :
424 : static PyMappingMethods structseq_as_mapping = {
425 : (lenfunc)structseq_length,
426 : (binaryfunc)structseq_subscript,
427 : };
428 :
429 : static PyMethodDef structseq_methods[] = {
430 : {"__reduce__", (PyCFunction)structseq_reduce,
431 : METH_NOARGS, NULL},
432 : {NULL, NULL}
433 : };
434 :
435 : static PyTypeObject _struct_sequence_template = {
436 : PyVarObject_HEAD_INIT(&PyType_Type, 0)
437 : NULL, /* tp_name */
438 : 0, /* tp_basicsize */
439 : 0, /* tp_itemsize */
440 : (destructor)structseq_dealloc, /* tp_dealloc */
441 : 0, /* tp_print */
442 : 0, /* tp_getattr */
443 : 0, /* tp_setattr */
444 : 0, /* tp_compare */
445 : (reprfunc)structseq_repr, /* tp_repr */
446 : 0, /* tp_as_number */
447 : &structseq_as_sequence, /* tp_as_sequence */
448 : &structseq_as_mapping, /* tp_as_mapping */
449 : structseq_hash, /* tp_hash */
450 : 0, /* tp_call */
451 : 0, /* tp_str */
452 : 0, /* tp_getattro */
453 : 0, /* tp_setattro */
454 : 0, /* tp_as_buffer */
455 : Py_TPFLAGS_DEFAULT, /* tp_flags */
456 : NULL, /* tp_doc */
457 : 0, /* tp_traverse */
458 : 0, /* tp_clear */
459 : structseq_richcompare, /* tp_richcompare */
460 : 0, /* tp_weaklistoffset */
461 : 0, /* tp_iter */
462 : 0, /* tp_iternext */
463 : structseq_methods, /* tp_methods */
464 : NULL, /* tp_members */
465 : 0, /* tp_getset */
466 : 0, /* tp_base */
467 : 0, /* tp_dict */
468 : 0, /* tp_descr_get */
469 : 0, /* tp_descr_set */
470 : 0, /* tp_dictoffset */
471 : 0, /* tp_init */
472 : 0, /* tp_alloc */
473 : structseq_new, /* tp_new */
474 : };
475 :
476 : void
477 24 : PyStructSequence_InitType(PyTypeObject *type, PyStructSequence_Desc *desc)
478 : {
479 : PyObject *dict;
480 : PyMemberDef* members;
481 : int n_members, n_unnamed_members, i, k;
482 :
483 : #ifdef Py_TRACE_REFS
484 : /* if the type object was chained, unchain it first
485 : before overwriting its storage */
486 : if (type->_ob_next) {
487 : _Py_ForgetReference((PyObject*)type);
488 : }
489 : #endif
490 :
491 24 : n_unnamed_members = 0;
492 252 : for (i = 0; desc->fields[i].name != NULL; ++i)
493 228 : if (desc->fields[i].name == PyStructSequence_UnnamedField)
494 9 : n_unnamed_members++;
495 24 : n_members = i;
496 :
497 24 : memcpy(type, &_struct_sequence_template, sizeof(PyTypeObject));
498 24 : type->tp_name = desc->name;
499 24 : type->tp_doc = desc->doc;
500 24 : type->tp_basicsize = sizeof(PyStructSequence)+
501 24 : sizeof(PyObject*)*(n_members-1);
502 24 : type->tp_itemsize = 0;
503 :
504 24 : members = PyMem_NEW(PyMemberDef, n_members-n_unnamed_members+1);
505 24 : if (members == NULL)
506 0 : return;
507 :
508 252 : for (i = k = 0; i < n_members; ++i) {
509 228 : if (desc->fields[i].name == PyStructSequence_UnnamedField)
510 9 : continue;
511 219 : members[k].name = desc->fields[i].name;
512 219 : members[k].type = T_OBJECT;
513 438 : members[k].offset = offsetof(PyStructSequence, ob_item)
514 219 : + i * sizeof(PyObject*);
515 219 : members[k].flags = READONLY;
516 219 : members[k].doc = desc->fields[i].doc;
517 219 : k++;
518 : }
519 24 : members[k].name = NULL;
520 :
521 24 : type->tp_members = members;
522 :
523 24 : if (PyType_Ready(type) < 0)
524 0 : return;
525 24 : Py_INCREF(type);
526 :
527 24 : dict = type->tp_dict;
528 : #define SET_DICT_FROM_INT(key, value) \
529 : do { \
530 : PyObject *v = PyInt_FromLong((long) value); \
531 : if (v != NULL) { \
532 : PyDict_SetItemString(dict, key, v); \
533 : Py_DECREF(v); \
534 : } \
535 : } while (0)
536 :
537 24 : SET_DICT_FROM_INT(visible_length_key, desc->n_in_sequence);
538 24 : SET_DICT_FROM_INT(real_length_key, n_members);
539 24 : SET_DICT_FROM_INT(unnamed_fields_key, n_unnamed_members);
540 : }
|