• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 *
PyStructSequence_New(PyTypeObject * type)30 PyStructSequence_New(PyTypeObject *type)
31 {
32     PyStructSequence *obj;
33 
34     obj = PyObject_New(PyStructSequence, type);
35     if (obj == NULL)
36         return NULL;
37     Py_SIZE(obj) = VISIBLE_SIZE_TP(type);
38 
39     return (PyObject*) obj;
40 }
41 
42 static void
structseq_dealloc(PyStructSequence * obj)43 structseq_dealloc(PyStructSequence *obj)
44 {
45     Py_ssize_t i, size;
46 
47     size = REAL_SIZE(obj);
48     for (i = 0; i < size; ++i) {
49         Py_XDECREF(obj->ob_item[i]);
50     }
51     PyObject_Del(obj);
52 }
53 
54 static Py_ssize_t
structseq_length(PyStructSequence * obj)55 structseq_length(PyStructSequence *obj)
56 {
57     return VISIBLE_SIZE(obj);
58 }
59 
60 static PyObject*
structseq_item(PyStructSequence * obj,Py_ssize_t i)61 structseq_item(PyStructSequence *obj, Py_ssize_t i)
62 {
63     if (i < 0 || i >= VISIBLE_SIZE(obj)) {
64         PyErr_SetString(PyExc_IndexError, "tuple index out of range");
65         return NULL;
66     }
67     Py_INCREF(obj->ob_item[i]);
68     return obj->ob_item[i];
69 }
70 
71 static PyObject*
structseq_slice(PyStructSequence * obj,Py_ssize_t low,Py_ssize_t high)72 structseq_slice(PyStructSequence *obj, Py_ssize_t low, Py_ssize_t high)
73 {
74     PyTupleObject *np;
75     Py_ssize_t i;
76 
77     if (low < 0)
78         low = 0;
79     if (high > VISIBLE_SIZE(obj))
80         high = VISIBLE_SIZE(obj);
81     if (high < low)
82         high = low;
83     np = (PyTupleObject *)PyTuple_New(high-low);
84     if (np == NULL)
85         return NULL;
86     for(i = low; i < high; ++i) {
87         PyObject *v = obj->ob_item[i];
88         Py_INCREF(v);
89         PyTuple_SET_ITEM(np, i-low, v);
90     }
91     return (PyObject *) np;
92 }
93 
94 static PyObject *
structseq_subscript(PyStructSequence * self,PyObject * item)95 structseq_subscript(PyStructSequence *self, PyObject *item)
96 {
97     if (PyIndex_Check(item)) {
98         Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
99         if (i == -1 && PyErr_Occurred())
100             return NULL;
101 
102         if (i < 0)
103             i += VISIBLE_SIZE(self);
104 
105         if (i < 0 || i >= VISIBLE_SIZE(self)) {
106             PyErr_SetString(PyExc_IndexError,
107                 "tuple index out of range");
108             return NULL;
109         }
110         Py_INCREF(self->ob_item[i]);
111         return self->ob_item[i];
112     }
113     else if (PySlice_Check(item)) {
114         Py_ssize_t start, stop, step, slicelen, cur, i;
115         PyObject *result;
116 
117         if (PySlice_GetIndicesEx((PySliceObject *)item,
118                                  VISIBLE_SIZE(self), &start, &stop,
119                                  &step, &slicelen) < 0) {
120             return NULL;
121         }
122         if (slicelen <= 0)
123             return PyTuple_New(0);
124         result = PyTuple_New(slicelen);
125         if (result == NULL)
126             return NULL;
127         for (cur = start, i = 0; i < slicelen;
128              cur += step, i++) {
129             PyObject *v = self->ob_item[cur];
130             Py_INCREF(v);
131             PyTuple_SET_ITEM(result, i, v);
132         }
133         return result;
134     }
135     else {
136         PyErr_SetString(PyExc_TypeError,
137                         "structseq index must be integer");
138         return NULL;
139     }
140 }
141 
142 static PyObject *
structseq_new(PyTypeObject * type,PyObject * args,PyObject * kwds)143 structseq_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
144 {
145     PyObject *arg = NULL;
146     PyObject *dict = NULL;
147     PyObject *ob;
148     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     if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:structseq",
153                                      kwlist, &arg, &dict))
154         return NULL;
155 
156     arg = PySequence_Fast(arg, "constructor requires a sequence");
157 
158     if (!arg) {
159         return NULL;
160     }
161 
162     if (dict && !PyDict_Check(dict)) {
163         PyErr_Format(PyExc_TypeError,
164                      "%.500s() takes a dict as second arg, if any",
165                      type->tp_name);
166         Py_DECREF(arg);
167         return NULL;
168     }
169 
170     len = PySequence_Fast_GET_SIZE(arg);
171     min_len = VISIBLE_SIZE_TP(type);
172     max_len = REAL_SIZE_TP(type);
173     n_unnamed_fields = UNNAMED_FIELDS_TP(type);
174 
175     if (min_len != max_len) {
176         if (len < min_len) {
177             PyErr_Format(PyExc_TypeError,
178                 "%.500s() takes an at least %zd-sequence (%zd-sequence given)",
179                 type->tp_name, min_len, len);
180             Py_DECREF(arg);
181             return NULL;
182         }
183 
184         if (len > max_len) {
185             PyErr_Format(PyExc_TypeError,
186                          "%.500s() takes an at most %zd-sequence (%zd-sequence given)",
187                          type->tp_name, max_len, len);
188             Py_DECREF(arg);
189             return NULL;
190         }
191     }
192     else {
193         if (len != min_len) {
194             PyErr_Format(PyExc_TypeError,
195                          "%.500s() takes a %zd-sequence (%zd-sequence given)",
196                          type->tp_name, min_len, len);
197             Py_DECREF(arg);
198             return NULL;
199         }
200     }
201 
202     res = (PyStructSequence*) PyStructSequence_New(type);
203     if (res == NULL) {
204         Py_DECREF(arg);
205         return NULL;
206     }
207     for (i = 0; i < len; ++i) {
208         PyObject *v = PySequence_Fast_GET_ITEM(arg, i);
209         Py_INCREF(v);
210         res->ob_item[i] = v;
211     }
212     for (; i < max_len; ++i) {
213         if (dict && (ob = PyDict_GetItemString(
214             dict, type->tp_members[i-n_unnamed_fields].name))) {
215         }
216         else {
217             ob = Py_None;
218         }
219         Py_INCREF(ob);
220         res->ob_item[i] = ob;
221     }
222 
223     Py_DECREF(arg);
224     return (PyObject*) res;
225 }
226 
227 static PyObject *
make_tuple(PyStructSequence * obj)228 make_tuple(PyStructSequence *obj)
229 {
230     return structseq_slice(obj, 0, VISIBLE_SIZE(obj));
231 }
232 
233 static PyObject *
structseq_repr(PyStructSequence * obj)234 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     PyTypeObject *typ = Py_TYPE(obj);
242     int i, removelast = 0;
243     Py_ssize_t len;
244     char buf[REPR_BUFFER_SIZE];
245     char *endofbuf, *pbuf = buf;
246 
247     /* pointer to end of writeable buffer; safes space for "...)\0" */
248     endofbuf= &buf[REPR_BUFFER_SIZE-5];
249 
250     if ((tup = make_tuple(obj)) == NULL) {
251         return NULL;
252     }
253 
254     /* "typename(", limited to  TYPE_MAXSIZE */
255     len = strlen(typ->tp_name) > TYPE_MAXSIZE ? TYPE_MAXSIZE :
256                             strlen(typ->tp_name);
257     strncpy(pbuf, typ->tp_name, len);
258     pbuf += len;
259     *pbuf++ = '(';
260 
261     for (i=0; i < VISIBLE_SIZE(obj); i++) {
262         PyObject *val, *repr;
263         char *cname, *crepr;
264 
265         cname = typ->tp_members[i].name;
266 
267         val = PyTuple_GetItem(tup, i);
268         if (cname == NULL || val == NULL) {
269             return NULL;
270         }
271         repr = PyObject_Repr(val);
272         if (repr == NULL) {
273             Py_DECREF(tup);
274             return NULL;
275         }
276         crepr = PyString_AsString(repr);
277         if (crepr == NULL) {
278             Py_DECREF(tup);
279             Py_DECREF(repr);
280             return NULL;
281         }
282 
283         /* + 3: keep space for "=" and ", " */
284         len = strlen(cname) + strlen(crepr) + 3;
285         if ((pbuf+len) <= endofbuf) {
286             strcpy(pbuf, cname);
287             pbuf += strlen(cname);
288             *pbuf++ = '=';
289             strcpy(pbuf, crepr);
290             pbuf += strlen(crepr);
291             *pbuf++ = ',';
292             *pbuf++ = ' ';
293             removelast = 1;
294             Py_DECREF(repr);
295         }
296         else {
297             strcpy(pbuf, "...");
298             pbuf += 3;
299             removelast = 0;
300             Py_DECREF(repr);
301             break;
302         }
303     }
304     Py_DECREF(tup);
305     if (removelast) {
306         /* overwrite last ", " */
307         pbuf-=2;
308     }
309     *pbuf++ = ')';
310     *pbuf = '\0';
311 
312     return PyString_FromString(buf);
313 }
314 
315 static PyObject *
structseq_concat(PyStructSequence * obj,PyObject * b)316 structseq_concat(PyStructSequence *obj, PyObject *b)
317 {
318     PyObject *tup, *result;
319     tup = make_tuple(obj);
320     result = PySequence_Concat(tup, b);
321     Py_DECREF(tup);
322     return result;
323 }
324 
325 static PyObject *
structseq_repeat(PyStructSequence * obj,Py_ssize_t n)326 structseq_repeat(PyStructSequence *obj, Py_ssize_t n)
327 {
328     PyObject *tup, *result;
329     tup = make_tuple(obj);
330     result = PySequence_Repeat(tup, n);
331     Py_DECREF(tup);
332     return result;
333 }
334 
335 static int
structseq_contains(PyStructSequence * obj,PyObject * o)336 structseq_contains(PyStructSequence *obj, PyObject *o)
337 {
338     PyObject *tup;
339     int result;
340     tup = make_tuple(obj);
341     if (!tup)
342         return -1;
343     result = PySequence_Contains(tup, o);
344     Py_DECREF(tup);
345     return result;
346 }
347 
348 static long
structseq_hash(PyObject * obj)349 structseq_hash(PyObject *obj)
350 {
351     PyObject *tup;
352     long result;
353     tup = make_tuple((PyStructSequence*) obj);
354     if (!tup)
355         return -1;
356     result = PyObject_Hash(tup);
357     Py_DECREF(tup);
358     return result;
359 }
360 
361 static PyObject *
structseq_richcompare(PyObject * obj,PyObject * o2,int op)362 structseq_richcompare(PyObject *obj, PyObject *o2, int op)
363 {
364     PyObject *tup, *result;
365     tup = make_tuple((PyStructSequence*) obj);
366     result = PyObject_RichCompare(tup, o2, op);
367     Py_DECREF(tup);
368     return result;
369 }
370 
371 static PyObject *
structseq_reduce(PyStructSequence * self)372 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     n_fields = REAL_SIZE(self);
381     n_visible_fields = VISIBLE_SIZE(self);
382     n_unnamed_fields = UNNAMED_FIELDS(self);
383     tup = PyTuple_New(n_visible_fields);
384     if (!tup) {
385         return NULL;
386     }
387 
388     dict = PyDict_New();
389     if (!dict) {
390         Py_DECREF(tup);
391         return NULL;
392     }
393 
394     for (i = 0; i < n_visible_fields; i++) {
395         Py_INCREF(self->ob_item[i]);
396         PyTuple_SET_ITEM(tup, i, self->ob_item[i]);
397     }
398 
399     for (; i < n_fields; i++) {
400         char *n = Py_TYPE(self)->tp_members[i-n_unnamed_fields].name;
401         PyDict_SetItemString(dict, n,
402                              self->ob_item[i]);
403     }
404 
405     result = Py_BuildValue("(O(OO))", Py_TYPE(self), tup, dict);
406 
407     Py_DECREF(tup);
408     Py_DECREF(dict);
409 
410     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
PyStructSequence_InitType(PyTypeObject * type,PyStructSequence_Desc * desc)477 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     n_unnamed_members = 0;
492     for (i = 0; desc->fields[i].name != NULL; ++i)
493         if (desc->fields[i].name == PyStructSequence_UnnamedField)
494             n_unnamed_members++;
495     n_members = i;
496 
497     memcpy(type, &_struct_sequence_template, sizeof(PyTypeObject));
498     type->tp_name = desc->name;
499     type->tp_doc = desc->doc;
500     type->tp_basicsize = sizeof(PyStructSequence)+
501         sizeof(PyObject*)*(n_members-1);
502     type->tp_itemsize = 0;
503 
504     members = PyMem_NEW(PyMemberDef, n_members-n_unnamed_members+1);
505     if (members == NULL)
506         return;
507 
508     for (i = k = 0; i < n_members; ++i) {
509         if (desc->fields[i].name == PyStructSequence_UnnamedField)
510             continue;
511         members[k].name = desc->fields[i].name;
512         members[k].type = T_OBJECT;
513         members[k].offset = offsetof(PyStructSequence, ob_item)
514           + i * sizeof(PyObject*);
515         members[k].flags = READONLY;
516         members[k].doc = desc->fields[i].doc;
517         k++;
518     }
519     members[k].name = NULL;
520 
521     type->tp_members = members;
522 
523     if (PyType_Ready(type) < 0)
524         return;
525     Py_INCREF(type);
526 
527     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     SET_DICT_FROM_INT(visible_length_key, desc->n_in_sequence);
538     SET_DICT_FROM_INT(real_length_key, n_members);
539     SET_DICT_FROM_INT(unnamed_fields_key, n_unnamed_members);
540 }
541