blob: 75e2250bf9c0342d67d2f30c45827b0a2f9d24b0 [file] [log] [blame]
Guido van Rossume82f75a2001-10-18 20:47:51 +00001/* 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"
Guido van Rossume82f75a2001-10-18 20:47:51 +00006
7static char visible_length_key[] = "n_sequence_fields";
8static char real_length_key[] = "n_fields";
Martin v. Löwisceaa77c2002-10-16 19:10:03 +00009static char unnamed_fields_key[] = "n_unnamed_fields";
Guido van Rossume82f75a2001-10-18 20:47:51 +000010
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000011/* Fields with this name have only a field index, not a field name.
Martin v. Löwisf607bda2002-10-16 18:27:39 +000012 They are only allowed for indices < n_visible_fields. */
13char *PyStructSequence_UnnamedField = "unnamed field";
14
Christian Heimes90aa7642007-12-19 02:45:37 +000015#define VISIBLE_SIZE(op) Py_SIZE(op)
Christian Heimes217cfd12007-12-02 14:31:20 +000016#define VISIBLE_SIZE_TP(tp) PyLong_AsLong( \
Guido van Rossume82f75a2001-10-18 20:47:51 +000017 PyDict_GetItemString((tp)->tp_dict, visible_length_key))
18
Christian Heimes217cfd12007-12-02 14:31:20 +000019#define REAL_SIZE_TP(tp) PyLong_AsLong( \
Guido van Rossume82f75a2001-10-18 20:47:51 +000020 PyDict_GetItemString((tp)->tp_dict, real_length_key))
Christian Heimes90aa7642007-12-19 02:45:37 +000021#define REAL_SIZE(op) REAL_SIZE_TP(Py_TYPE(op))
Guido van Rossume82f75a2001-10-18 20:47:51 +000022
Christian Heimes217cfd12007-12-02 14:31:20 +000023#define UNNAMED_FIELDS_TP(tp) PyLong_AsLong( \
Martin v. Löwisceaa77c2002-10-16 19:10:03 +000024 PyDict_GetItemString((tp)->tp_dict, unnamed_fields_key))
Christian Heimes90aa7642007-12-19 02:45:37 +000025#define UNNAMED_FIELDS(op) UNNAMED_FIELDS_TP(Py_TYPE(op))
Martin v. Löwisceaa77c2002-10-16 19:10:03 +000026
Guido van Rossume82f75a2001-10-18 20:47:51 +000027
28PyObject *
29PyStructSequence_New(PyTypeObject *type)
30{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000031 PyStructSequence *obj;
Benjamin Petersonccabcd42010-07-07 20:54:01 +000032 Py_ssize_t size = REAL_SIZE_TP(type), i;
Christian Heimesd32ed6f2008-01-14 18:49:24 +000033
Benjamin Petersonccabcd42010-07-07 20:54:01 +000034 obj = PyObject_GC_NewVar(PyStructSequence, type, size);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000035 if (obj == NULL)
36 return NULL;
Benjamin Petersond02441e2010-07-08 22:33:03 +000037 /* Hack the size of the variable object, so invisible fields don't appear
38 to Python code. */
39 Py_SIZE(obj) = VISIBLE_SIZE_TP(type);
Benjamin Petersonccabcd42010-07-07 20:54:01 +000040 for (i = 0; i < size; i++)
41 obj->ob_item[i] = NULL;
Guido van Rossume82f75a2001-10-18 20:47:51 +000042
Benjamin Petersonccabcd42010-07-07 20:54:01 +000043 return (PyObject*)obj;
Thomas Woutersed03b412007-08-28 21:37:11 +000044}
45
Benjamin Petersond02441e2010-07-08 22:33:03 +000046static void
47structseq_dealloc(PyStructSequence *obj)
48{
49 Py_ssize_t i, size;
50
51 size = REAL_SIZE(obj);
52 for (i = 0; i < size; ++i) {
53 Py_XDECREF(obj->ob_item[i]);
54 }
55 PyObject_GC_Del(obj);
56}
57
Thomas Woutersed03b412007-08-28 21:37:11 +000058static PyObject *
Guido van Rossume82f75a2001-10-18 20:47:51 +000059structseq_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
60{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000061 PyObject *arg = NULL;
62 PyObject *dict = NULL;
63 PyObject *ob;
64 PyStructSequence *res = NULL;
65 Py_ssize_t len, min_len, max_len, i, n_unnamed_fields;
66 static char *kwlist[] = {"sequence", "dict", 0};
Guido van Rossume82f75a2001-10-18 20:47:51 +000067
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000068 if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:structseq",
69 kwlist, &arg, &dict))
70 return NULL;
Guido van Rossume82f75a2001-10-18 20:47:51 +000071
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000072 arg = PySequence_Fast(arg, "constructor requires a sequence");
Michael W. Hudsonce358e32002-03-06 17:07:49 +000073
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000074 if (!arg) {
75 return NULL;
76 }
Guido van Rossume82f75a2001-10-18 20:47:51 +000077
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000078 if (dict && !PyDict_Check(dict)) {
79 PyErr_Format(PyExc_TypeError,
80 "%.500s() takes a dict as second arg, if any",
81 type->tp_name);
82 Py_DECREF(arg);
83 return NULL;
84 }
Guido van Rossume82f75a2001-10-18 20:47:51 +000085
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000086 len = PySequence_Fast_GET_SIZE(arg);
87 min_len = VISIBLE_SIZE_TP(type);
88 max_len = REAL_SIZE_TP(type);
89 n_unnamed_fields = UNNAMED_FIELDS_TP(type);
Michael W. Hudsonce358e32002-03-06 17:07:49 +000090
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000091 if (min_len != max_len) {
92 if (len < min_len) {
93 PyErr_Format(PyExc_TypeError,
94 "%.500s() takes an at least %zd-sequence (%zd-sequence given)",
95 type->tp_name, min_len, len);
96 Py_DECREF(arg);
97 return NULL;
98 }
Michael W. Hudsonce358e32002-03-06 17:07:49 +000099
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000100 if (len > max_len) {
101 PyErr_Format(PyExc_TypeError,
102 "%.500s() takes an at most %zd-sequence (%zd-sequence given)",
103 type->tp_name, max_len, len);
104 Py_DECREF(arg);
105 return NULL;
106 }
107 }
108 else {
109 if (len != min_len) {
110 PyErr_Format(PyExc_TypeError,
111 "%.500s() takes a %zd-sequence (%zd-sequence given)",
112 type->tp_name, min_len, len);
113 Py_DECREF(arg);
114 return NULL;
115 }
116 }
Michael W. Hudsonce358e32002-03-06 17:07:49 +0000117
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000118 res = (PyStructSequence*) PyStructSequence_New(type);
119 if (res == NULL) {
120 return NULL;
121 }
122 for (i = 0; i < len; ++i) {
123 PyObject *v = PySequence_Fast_GET_ITEM(arg, i);
124 Py_INCREF(v);
125 res->ob_item[i] = v;
126 }
127 for (; i < max_len; ++i) {
128 if (dict && (ob = PyDict_GetItemString(
129 dict, type->tp_members[i-n_unnamed_fields].name))) {
130 }
131 else {
132 ob = Py_None;
133 }
134 Py_INCREF(ob);
135 res->ob_item[i] = ob;
136 }
137
138 Py_DECREF(arg);
139 return (PyObject*) res;
Guido van Rossume82f75a2001-10-18 20:47:51 +0000140}
141
Guido van Rossume82f75a2001-10-18 20:47:51 +0000142
143static PyObject *
144structseq_repr(PyStructSequence *obj)
145{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000146 /* buffer and type size were chosen well considered. */
Christian Heimesd32ed6f2008-01-14 18:49:24 +0000147#define REPR_BUFFER_SIZE 512
148#define TYPE_MAXSIZE 100
149
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000150 PyTypeObject *typ = Py_TYPE(obj);
151 int i, removelast = 0;
152 Py_ssize_t len;
153 char buf[REPR_BUFFER_SIZE];
154 char *endofbuf, *pbuf = buf;
Christian Heimesd32ed6f2008-01-14 18:49:24 +0000155
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000156 /* pointer to end of writeable buffer; safes space for "...)\0" */
157 endofbuf= &buf[REPR_BUFFER_SIZE-5];
Christian Heimesd32ed6f2008-01-14 18:49:24 +0000158
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000159 /* "typename(", limited to TYPE_MAXSIZE */
160 len = strlen(typ->tp_name) > TYPE_MAXSIZE ? TYPE_MAXSIZE :
161 strlen(typ->tp_name);
162 strncpy(pbuf, typ->tp_name, len);
163 pbuf += len;
164 *pbuf++ = '(';
Christian Heimesd32ed6f2008-01-14 18:49:24 +0000165
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000166 for (i=0; i < VISIBLE_SIZE(obj); i++) {
167 PyObject *val, *repr;
168 char *cname, *crepr;
Christian Heimesd32ed6f2008-01-14 18:49:24 +0000169
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000170 cname = typ->tp_members[i].name;
Benjamin Petersond02441e2010-07-08 22:33:03 +0000171 if (cname == NULL) {
172 PyErr_Format(PyExc_SystemError, "In structseq_repr(), member %d name is NULL"
173 " for type %.500s", i, typ->tp_name);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000174 return NULL;
Benjamin Petersond02441e2010-07-08 22:33:03 +0000175 }
Benjamin Petersonccabcd42010-07-07 20:54:01 +0000176 val = PyStructSequence_GET_ITEM(obj, i);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000177 repr = PyObject_Repr(val);
Benjamin Petersonccabcd42010-07-07 20:54:01 +0000178 if (repr == NULL)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000179 return NULL;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000180 crepr = _PyUnicode_AsString(repr);
181 if (crepr == NULL) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000182 Py_DECREF(repr);
183 return NULL;
184 }
185
186 /* + 3: keep space for "=" and ", " */
187 len = strlen(cname) + strlen(crepr) + 3;
188 if ((pbuf+len) <= endofbuf) {
189 strcpy(pbuf, cname);
190 pbuf += strlen(cname);
191 *pbuf++ = '=';
192 strcpy(pbuf, crepr);
193 pbuf += strlen(crepr);
194 *pbuf++ = ',';
195 *pbuf++ = ' ';
196 removelast = 1;
197 Py_DECREF(repr);
198 }
199 else {
200 strcpy(pbuf, "...");
201 pbuf += 3;
202 removelast = 0;
203 Py_DECREF(repr);
204 break;
205 }
206 }
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000207 if (removelast) {
208 /* overwrite last ", " */
209 pbuf-=2;
210 }
211 *pbuf++ = ')';
212 *pbuf = '\0';
213
214 return PyUnicode_FromString(buf);
Guido van Rossume82f75a2001-10-18 20:47:51 +0000215}
216
217static PyObject *
Michael W. Hudson7bb466a2002-03-05 13:27:58 +0000218structseq_reduce(PyStructSequence* self)
219{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000220 PyObject* tup;
221 PyObject* dict;
222 PyObject* result;
223 Py_ssize_t n_fields, n_visible_fields, n_unnamed_fields;
224 int i;
Michael W. Hudson7bb466a2002-03-05 13:27:58 +0000225
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000226 n_fields = REAL_SIZE(self);
227 n_visible_fields = VISIBLE_SIZE(self);
228 n_unnamed_fields = UNNAMED_FIELDS(self);
229 tup = PyTuple_New(n_visible_fields);
230 if (!tup) {
231 return NULL;
232 }
Michael W. Hudsonce358e32002-03-06 17:07:49 +0000233
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000234 dict = PyDict_New();
235 if (!dict) {
236 Py_DECREF(tup);
237 return NULL;
238 }
Michael W. Hudsonce358e32002-03-06 17:07:49 +0000239
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000240 for (i = 0; i < n_visible_fields; i++) {
241 Py_INCREF(self->ob_item[i]);
242 PyTuple_SET_ITEM(tup, i, self->ob_item[i]);
243 }
Michael W. Hudson70ffddf2002-03-07 15:13:40 +0000244
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000245 for (; i < n_fields; i++) {
246 char *n = Py_TYPE(self)->tp_members[i-n_unnamed_fields].name;
247 PyDict_SetItemString(dict, n,
248 self->ob_item[i]);
249 }
Michael W. Hudson70ffddf2002-03-07 15:13:40 +0000250
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000251 result = Py_BuildValue("(O(OO))", Py_TYPE(self), tup, dict);
252
253 Py_DECREF(tup);
254 Py_DECREF(dict);
255
256 return result;
Michael W. Hudson7bb466a2002-03-05 13:27:58 +0000257}
258
Michael W. Hudson7bb466a2002-03-05 13:27:58 +0000259static PyMethodDef structseq_methods[] = {
Benjamin Petersonccabcd42010-07-07 20:54:01 +0000260 {"__reduce__", (PyCFunction)structseq_reduce, METH_NOARGS, NULL},
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000261 {NULL, NULL}
Michael W. Hudson7bb466a2002-03-05 13:27:58 +0000262};
263
Guido van Rossume82f75a2001-10-18 20:47:51 +0000264static PyTypeObject _struct_sequence_template = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000265 PyVarObject_HEAD_INIT(&PyType_Type, 0)
266 NULL, /* tp_name */
Benjamin Petersonccabcd42010-07-07 20:54:01 +0000267 sizeof(PyStructSequence) - sizeof(PyObject *), /* tp_basicsize */
268 sizeof(PyObject *), /* tp_itemsize */
Benjamin Petersond02441e2010-07-08 22:33:03 +0000269 (destructor)structseq_dealloc, /* tp_dealloc */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000270 0, /* tp_print */
271 0, /* tp_getattr */
272 0, /* tp_setattr */
273 0, /* tp_reserved */
274 (reprfunc)structseq_repr, /* tp_repr */
275 0, /* tp_as_number */
Benjamin Petersonccabcd42010-07-07 20:54:01 +0000276 0, /* tp_as_sequence */
277 0, /* tp_as_mapping */
278 0, /* tp_hash */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000279 0, /* tp_call */
280 0, /* tp_str */
281 0, /* tp_getattro */
282 0, /* tp_setattro */
283 0, /* tp_as_buffer */
Benjamin Petersonccabcd42010-07-07 20:54:01 +0000284 Py_TPFLAGS_DEFAULT, /* tp_flags */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000285 NULL, /* tp_doc */
286 0, /* tp_traverse */
287 0, /* tp_clear */
Benjamin Petersonccabcd42010-07-07 20:54:01 +0000288 0, /* tp_richcompare */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000289 0, /* tp_weaklistoffset */
290 0, /* tp_iter */
291 0, /* tp_iternext */
292 structseq_methods, /* tp_methods */
293 NULL, /* tp_members */
294 0, /* tp_getset */
295 0, /* tp_base */
296 0, /* tp_dict */
297 0, /* tp_descr_get */
298 0, /* tp_descr_set */
299 0, /* tp_dictoffset */
300 0, /* tp_init */
301 0, /* tp_alloc */
302 structseq_new, /* tp_new */
Guido van Rossume82f75a2001-10-18 20:47:51 +0000303};
304
305void
306PyStructSequence_InitType(PyTypeObject *type, PyStructSequence_Desc *desc)
307{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000308 PyObject *dict;
309 PyMemberDef* members;
310 int n_members, n_unnamed_members, i, k;
Guido van Rossume82f75a2001-10-18 20:47:51 +0000311
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000312#ifdef Py_TRACE_REFS
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000313 /* if the type object was chained, unchain it first
314 before overwriting its storage */
315 if (type->ob_base.ob_base._ob_next) {
316 _Py_ForgetReference((PyObject*)type);
317 }
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000318#endif
319
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000320 n_unnamed_members = 0;
321 for (i = 0; desc->fields[i].name != NULL; ++i)
322 if (desc->fields[i].name == PyStructSequence_UnnamedField)
323 n_unnamed_members++;
324 n_members = i;
Guido van Rossume82f75a2001-10-18 20:47:51 +0000325
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000326 memcpy(type, &_struct_sequence_template, sizeof(PyTypeObject));
Benjamin Petersonccabcd42010-07-07 20:54:01 +0000327 type->tp_base = &PyTuple_Type;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000328 type->tp_name = desc->name;
329 type->tp_doc = desc->doc;
Guido van Rossume82f75a2001-10-18 20:47:51 +0000330
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000331 members = PyMem_NEW(PyMemberDef, n_members-n_unnamed_members+1);
332 if (members == NULL)
333 return;
Guido van Rossume82f75a2001-10-18 20:47:51 +0000334
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000335 for (i = k = 0; i < n_members; ++i) {
336 if (desc->fields[i].name == PyStructSequence_UnnamedField)
337 continue;
338 members[k].name = desc->fields[i].name;
339 members[k].type = T_OBJECT;
340 members[k].offset = offsetof(PyStructSequence, ob_item)
341 + i * sizeof(PyObject*);
342 members[k].flags = READONLY;
343 members[k].doc = desc->fields[i].doc;
344 k++;
345 }
346 members[k].name = NULL;
Guido van Rossume82f75a2001-10-18 20:47:51 +0000347
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000348 type->tp_members = members;
Guido van Rossume82f75a2001-10-18 20:47:51 +0000349
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000350 if (PyType_Ready(type) < 0)
351 return;
352 Py_INCREF(type);
Neal Norwitz2f99b242008-08-24 05:48:10 +0000353
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000354 dict = type->tp_dict;
355#define SET_DICT_FROM_INT(key, value) \
356 do { \
357 PyObject *v = PyLong_FromLong((long) value); \
358 if (v != NULL) { \
359 PyDict_SetItemString(dict, key, v); \
360 Py_DECREF(v); \
361 } \
362 } while (0)
363
364 SET_DICT_FROM_INT(visible_length_key, desc->n_in_sequence);
365 SET_DICT_FROM_INT(real_length_key, n_members);
366 SET_DICT_FROM_INT(unnamed_fields_key, n_unnamed_members);
Guido van Rossume82f75a2001-10-18 20:47:51 +0000367}