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