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_Unpack(item, &start, &stop, &step) < 0) {
118             return NULL;
119         }
120         slicelen = _PySlice_AdjustIndices(VISIBLE_SIZE(self), &start, &stop,
121                                          step);
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);
256     if (len > TYPE_MAXSIZE) {
257         len = TYPE_MAXSIZE;
258     }
259     pbuf = memcpy(pbuf, typ->tp_name, len);
260     pbuf += len;
261     *pbuf++ = '(';
262 
263     for (i=0; i < VISIBLE_SIZE(obj); i++) {
264         PyObject *val, *repr;
265         char *cname, *crepr;
266 
267         cname = typ->tp_members[i].name;
268 
269         val = PyTuple_GetItem(tup, i);
270         if (cname == NULL || val == NULL) {
271             Py_DECREF(tup);
272             return NULL;
273         }
274         repr = PyObject_Repr(val);
275         if (repr == NULL) {
276             Py_DECREF(tup);
277             return NULL;
278         }
279         crepr = PyString_AsString(repr);
280         if (crepr == NULL) {
281             Py_DECREF(tup);
282             Py_DECREF(repr);
283             return NULL;
284         }
285 
286         /* + 3: keep space for "=" and ", " */
287         len = strlen(cname) + strlen(crepr) + 3;
288         if ((pbuf+len) <= endofbuf) {
289             strcpy(pbuf, cname);
290             pbuf += strlen(cname);
291             *pbuf++ = '=';
292             strcpy(pbuf, crepr);
293             pbuf += strlen(crepr);
294             *pbuf++ = ',';
295             *pbuf++ = ' ';
296             removelast = 1;
297             Py_DECREF(repr);
298         }
299         else {
300             strcpy(pbuf, "...");
301             pbuf += 3;
302             removelast = 0;
303             Py_DECREF(repr);
304             break;
305         }
306     }
307     Py_DECREF(tup);
308     if (removelast) {
309         /* overwrite last ", " */
310         pbuf-=2;
311     }
312     *pbuf++ = ')';
313     *pbuf = '\0';
314 
315     return PyString_FromString(buf);
316 }
317 
318 static PyObject *
structseq_concat(PyStructSequence * obj,PyObject * b)319 structseq_concat(PyStructSequence *obj, PyObject *b)
320 {
321     PyObject *tup, *result;
322     tup = make_tuple(obj);
323     result = PySequence_Concat(tup, b);
324     Py_DECREF(tup);
325     return result;
326 }
327 
328 static PyObject *
structseq_repeat(PyStructSequence * obj,Py_ssize_t n)329 structseq_repeat(PyStructSequence *obj, Py_ssize_t n)
330 {
331     PyObject *tup, *result;
332     tup = make_tuple(obj);
333     result = PySequence_Repeat(tup, n);
334     Py_DECREF(tup);
335     return result;
336 }
337 
338 static int
structseq_contains(PyStructSequence * obj,PyObject * o)339 structseq_contains(PyStructSequence *obj, PyObject *o)
340 {
341     PyObject *tup;
342     int result;
343     tup = make_tuple(obj);
344     if (!tup)
345         return -1;
346     result = PySequence_Contains(tup, o);
347     Py_DECREF(tup);
348     return result;
349 }
350 
351 static long
structseq_hash(PyObject * obj)352 structseq_hash(PyObject *obj)
353 {
354     PyObject *tup;
355     long result;
356     tup = make_tuple((PyStructSequence*) obj);
357     if (!tup)
358         return -1;
359     result = PyObject_Hash(tup);
360     Py_DECREF(tup);
361     return result;
362 }
363 
364 static PyObject *
structseq_richcompare(PyObject * obj,PyObject * o2,int op)365 structseq_richcompare(PyObject *obj, PyObject *o2, int op)
366 {
367     PyObject *tup, *result;
368     tup = make_tuple((PyStructSequence*) obj);
369     result = PyObject_RichCompare(tup, o2, op);
370     Py_DECREF(tup);
371     return result;
372 }
373 
374 static PyObject *
structseq_reduce(PyStructSequence * self)375 structseq_reduce(PyStructSequence* self)
376 {
377     PyObject* tup;
378     PyObject* dict;
379     PyObject* result;
380     Py_ssize_t n_fields, n_visible_fields, n_unnamed_fields;
381     int i;
382 
383     n_fields = REAL_SIZE(self);
384     n_visible_fields = VISIBLE_SIZE(self);
385     n_unnamed_fields = UNNAMED_FIELDS(self);
386     tup = PyTuple_New(n_visible_fields);
387     if (!tup) {
388         return NULL;
389     }
390 
391     dict = PyDict_New();
392     if (!dict) {
393         Py_DECREF(tup);
394         return NULL;
395     }
396 
397     for (i = 0; i < n_visible_fields; i++) {
398         Py_INCREF(self->ob_item[i]);
399         PyTuple_SET_ITEM(tup, i, self->ob_item[i]);
400     }
401 
402     for (; i < n_fields; i++) {
403         char *n = Py_TYPE(self)->tp_members[i-n_unnamed_fields].name;
404         PyDict_SetItemString(dict, n,
405                              self->ob_item[i]);
406     }
407 
408     result = Py_BuildValue("(O(OO))", Py_TYPE(self), tup, dict);
409 
410     Py_DECREF(tup);
411     Py_DECREF(dict);
412 
413     return result;
414 }
415 
416 static PySequenceMethods structseq_as_sequence = {
417     (lenfunc)structseq_length,
418     (binaryfunc)structseq_concat,           /* sq_concat */
419     (ssizeargfunc)structseq_repeat,         /* sq_repeat */
420     (ssizeargfunc)structseq_item,               /* sq_item */
421     (ssizessizeargfunc)structseq_slice,         /* sq_slice */
422     0,                                          /* sq_ass_item */
423     0,                                          /* sq_ass_slice */
424     (objobjproc)structseq_contains,             /* sq_contains */
425 };
426 
427 static PyMappingMethods structseq_as_mapping = {
428     (lenfunc)structseq_length,
429     (binaryfunc)structseq_subscript,
430 };
431 
432 static PyMethodDef structseq_methods[] = {
433     {"__reduce__", (PyCFunction)structseq_reduce,
434      METH_NOARGS, NULL},
435     {NULL, NULL}
436 };
437 
438 static PyTypeObject _struct_sequence_template = {
439     PyVarObject_HEAD_INIT(&PyType_Type, 0)
440     NULL,                                       /* tp_name */
441     0,                                          /* tp_basicsize */
442     0,                                          /* tp_itemsize */
443     (destructor)structseq_dealloc,              /* tp_dealloc */
444     0,                                          /* tp_print */
445     0,                                          /* tp_getattr */
446     0,                                          /* tp_setattr */
447     0,                                          /* tp_compare */
448     (reprfunc)structseq_repr,                   /* tp_repr */
449     0,                                          /* tp_as_number */
450     &structseq_as_sequence,                     /* tp_as_sequence */
451     &structseq_as_mapping,                      /* tp_as_mapping */
452     structseq_hash,                             /* tp_hash */
453     0,                                          /* tp_call */
454     0,                                          /* tp_str */
455     0,                                          /* tp_getattro */
456     0,                                          /* tp_setattro */
457     0,                                          /* tp_as_buffer */
458     Py_TPFLAGS_DEFAULT,                     /* tp_flags */
459     NULL,                                       /* tp_doc */
460     0,                                          /* tp_traverse */
461     0,                                          /* tp_clear */
462     structseq_richcompare,                      /* tp_richcompare */
463     0,                                          /* tp_weaklistoffset */
464     0,                                          /* tp_iter */
465     0,                                          /* tp_iternext */
466     structseq_methods,                          /* tp_methods */
467     NULL,                                       /* tp_members */
468     0,                                          /* tp_getset */
469     0,                                          /* tp_base */
470     0,                                          /* tp_dict */
471     0,                                          /* tp_descr_get */
472     0,                                          /* tp_descr_set */
473     0,                                          /* tp_dictoffset */
474     0,                                          /* tp_init */
475     0,                                          /* tp_alloc */
476     structseq_new,                              /* tp_new */
477 };
478 
479 void
PyStructSequence_InitType(PyTypeObject * type,PyStructSequence_Desc * desc)480 PyStructSequence_InitType(PyTypeObject *type, PyStructSequence_Desc *desc)
481 {
482     PyObject *dict;
483     PyMemberDef* members;
484     int n_members, n_unnamed_members, i, k;
485 
486 #ifdef Py_TRACE_REFS
487     /* if the type object was chained, unchain it first
488        before overwriting its storage */
489     if (type->_ob_next) {
490         _Py_ForgetReference((PyObject*)type);
491     }
492 #endif
493 
494     n_unnamed_members = 0;
495     for (i = 0; desc->fields[i].name != NULL; ++i)
496         if (desc->fields[i].name == PyStructSequence_UnnamedField)
497             n_unnamed_members++;
498     n_members = i;
499 
500     memcpy(type, &_struct_sequence_template, sizeof(PyTypeObject));
501     type->tp_name = desc->name;
502     type->tp_doc = desc->doc;
503     type->tp_basicsize = sizeof(PyStructSequence)+
504         sizeof(PyObject*)*(n_members-1);
505     type->tp_itemsize = 0;
506 
507     members = PyMem_NEW(PyMemberDef, n_members-n_unnamed_members+1);
508     if (members == NULL)
509         return;
510 
511     for (i = k = 0; i < n_members; ++i) {
512         if (desc->fields[i].name == PyStructSequence_UnnamedField)
513             continue;
514         members[k].name = desc->fields[i].name;
515         members[k].type = T_OBJECT;
516         members[k].offset = offsetof(PyStructSequence, ob_item)
517           + i * sizeof(PyObject*);
518         members[k].flags = READONLY;
519         members[k].doc = desc->fields[i].doc;
520         k++;
521     }
522     members[k].name = NULL;
523 
524     type->tp_members = members;
525 
526     if (PyType_Ready(type) < 0)
527         return;
528     Py_INCREF(type);
529 
530     dict = type->tp_dict;
531 #define SET_DICT_FROM_INT(key, value)                           \
532     do {                                                        \
533         PyObject *v = PyInt_FromLong((long) value);             \
534         if (v != NULL) {                                        \
535             PyDict_SetItemString(dict, key, v);                 \
536             Py_DECREF(v);                                       \
537         }                                                       \
538     } while (0)
539 
540     SET_DICT_FROM_INT(visible_length_key, desc->n_in_sequence);
541     SET_DICT_FROM_INT(real_length_key, n_members);
542     SET_DICT_FROM_INT(unnamed_fields_key, n_unnamed_members);
543 }
544