blob: fdc56cb793d7788b4cdbcc67e2995ab7dcc6dc12 [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"
6#include "structseq.h"
7
8static char visible_length_key[] = "n_sequence_fields";
9static char real_length_key[] = "n_fields";
10
11#define VISIBLE_SIZE(op) ((op)->ob_size)
12#define VISIBLE_SIZE_TP(tp) PyInt_AsLong( \
13 PyDict_GetItemString((tp)->tp_dict, visible_length_key))
14
15#define REAL_SIZE_TP(tp) PyInt_AsLong( \
16 PyDict_GetItemString((tp)->tp_dict, real_length_key))
17#define REAL_SIZE(op) REAL_SIZE_TP((op)->ob_type)
18
19
20PyObject *
21PyStructSequence_New(PyTypeObject *type)
22{
23 PyStructSequence *obj;
24
Neil Schemenauer7465ad22002-04-12 03:05:37 +000025 obj = PyObject_New(PyStructSequence, type);
Guido van Rossume82f75a2001-10-18 20:47:51 +000026 obj->ob_size = VISIBLE_SIZE_TP(type);
27
28 return (PyObject*) obj;
29}
30
31static void
32structseq_dealloc(PyStructSequence *obj)
33{
34 int i, size;
35
36 size = REAL_SIZE(obj);
37 for (i = 0; i < size; ++i) {
38 Py_XDECREF(obj->ob_item[i]);
39 }
Neil Schemenauer7465ad22002-04-12 03:05:37 +000040 PyObject_Del(obj);
Guido van Rossume82f75a2001-10-18 20:47:51 +000041}
42
43static int
44structseq_length(PyStructSequence *obj)
45{
46 return VISIBLE_SIZE(obj);
47}
48
49static PyObject*
50structseq_item(PyStructSequence *obj, int i)
51{
52 if (i < 0 || i >= VISIBLE_SIZE(obj)) {
53 PyErr_SetString(PyExc_IndexError, "tuple index out of range");
54 return NULL;
55 }
56 Py_INCREF(obj->ob_item[i]);
57 return obj->ob_item[i];
58}
59
60static PyObject*
61structseq_slice(PyStructSequence *obj, int low, int high)
62{
63 PyTupleObject *np;
64 int i;
65
66 if (low < 0)
67 low = 0;
68 if (high > VISIBLE_SIZE(obj))
69 high = VISIBLE_SIZE(obj);
70 if (high < low)
71 high = low;
72 np = (PyTupleObject *)PyTuple_New(high-low);
73 if (np == NULL)
74 return NULL;
75 for(i = low; i < high; ++i) {
76 PyObject *v = obj->ob_item[i];
77 Py_INCREF(v);
Tim Petersc2fe6182001-10-30 23:20:46 +000078 PyTuple_SET_ITEM(np, i-low, v);
Guido van Rossume82f75a2001-10-18 20:47:51 +000079 }
80 return (PyObject *) np;
81}
82
83static PyObject *
84structseq_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
85{
86 PyObject *arg = NULL;
Michael W. Hudsonce358e32002-03-06 17:07:49 +000087 PyObject *dict = NULL;
88 PyObject *ob;
Guido van Rossume82f75a2001-10-18 20:47:51 +000089 PyStructSequence *res = NULL;
Michael W. Hudsonce358e32002-03-06 17:07:49 +000090 int len, min_len, max_len, i;
91 static char *kwlist[] = {"sequence", "dict", 0};
Guido van Rossume82f75a2001-10-18 20:47:51 +000092
Michael W. Hudsonce358e32002-03-06 17:07:49 +000093 if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:structseq",
94 kwlist, &arg, &dict))
Guido van Rossume82f75a2001-10-18 20:47:51 +000095 return NULL;
96
Michael W. Hudsonce358e32002-03-06 17:07:49 +000097 arg = PySequence_Fast(arg, "constructor requires a sequence");
98
99 if (!arg) {
Guido van Rossume82f75a2001-10-18 20:47:51 +0000100 return NULL;
101 }
102
Michael W. Hudsonce358e32002-03-06 17:07:49 +0000103 if (dict && !PyDict_Check(dict)) {
104 PyErr_Format(PyExc_TypeError,
105 "%.500s() takes a dict as second arg, if any",
106 type->tp_name);
107 Py_DECREF(arg);
Guido van Rossume82f75a2001-10-18 20:47:51 +0000108 return NULL;
109 }
110
Michael W. Hudsonce358e32002-03-06 17:07:49 +0000111 len = PySequence_Fast_GET_SIZE(arg);
112 min_len = VISIBLE_SIZE_TP(type);
113 max_len = REAL_SIZE_TP(type);
114
115 if (min_len != max_len) {
116 if (len < min_len) {
117 PyErr_Format(PyExc_TypeError,
118 "%.500s() takes an at least %d-sequence (%d-sequence given)",
119 type->tp_name, min_len, len);
120 Py_DECREF(arg);
121 return NULL;
122 }
123
124 if (len > max_len) {
125 PyErr_Format(PyExc_TypeError,
126 "%.500s() takes an at most %d-sequence (%d-sequence given)",
127 type->tp_name, max_len, len);
128 Py_DECREF(arg);
129 return NULL;
130 }
131 }
132 else {
133 if (len != min_len) {
134 PyErr_Format(PyExc_TypeError,
135 "%.500s() takes a %d-sequence (%d-sequence given)",
136 type->tp_name, min_len, len);
137 Py_DECREF(arg);
138 return NULL;
139 }
140 }
141
Guido van Rossume82f75a2001-10-18 20:47:51 +0000142 res = (PyStructSequence*) PyStructSequence_New(type);
143 for (i = 0; i < len; ++i) {
Michael W. Hudsonce358e32002-03-06 17:07:49 +0000144 PyObject *v = PySequence_Fast_GET_ITEM(arg, i);
145 Py_INCREF(v);
146 res->ob_item[i] = v;
147 }
148 for (; i < max_len; ++i) {
149 if (dict && (ob = PyDict_GetItemString(
150 dict, type->tp_members[i].name))) {
151 }
152 else {
153 ob = Py_None;
154 }
155 Py_INCREF(ob);
156 res->ob_item[i] = ob;
Guido van Rossume82f75a2001-10-18 20:47:51 +0000157 }
158
Michael W. Hudsonce358e32002-03-06 17:07:49 +0000159 Py_DECREF(arg);
Guido van Rossume82f75a2001-10-18 20:47:51 +0000160 return (PyObject*) res;
161}
162
163static PyObject *
164make_tuple(PyStructSequence *obj)
165{
166 return structseq_slice(obj, 0, VISIBLE_SIZE(obj));
167}
168
169static PyObject *
170structseq_repr(PyStructSequence *obj)
171{
172 PyObject *tup, *str;
173 tup = make_tuple(obj);
174 str = PyObject_Repr(tup);
175 Py_DECREF(tup);
176 return str;
177}
178
179static PyObject *
180structseq_concat(PyStructSequence *obj, PyObject *b)
181{
182 PyObject *tup, *result;
183 tup = make_tuple(obj);
184 result = PySequence_Concat(tup, b);
185 Py_DECREF(tup);
186 return result;
187}
188
189static PyObject *
190structseq_repeat(PyStructSequence *obj, int n)
191{
192 PyObject *tup, *result;
193 tup = make_tuple(obj);
194 result = PySequence_Repeat(tup, n);
195 Py_DECREF(tup);
196 return result;
197}
198
199static int
200structseq_contains(PyStructSequence *obj, PyObject *o)
201{
202 PyObject *tup;
203 int result;
204 tup = make_tuple(obj);
205 result = PySequence_Contains(tup, o);
206 Py_DECREF(tup);
207 return result;
208}
209
210static long
211structseq_hash(PyObject *obj)
212{
213 PyObject *tup;
214 long result;
215 tup = make_tuple((PyStructSequence*) obj);
216 result = PyObject_Hash(tup);
217 Py_DECREF(tup);
218 return result;
219}
220
221static PyObject *
222structseq_richcompare(PyObject *obj, PyObject *o2, int op)
223{
224 PyObject *tup, *result;
225 tup = make_tuple((PyStructSequence*) obj);
226 result = PyObject_RichCompare(tup, o2, op);
227 Py_DECREF(tup);
228 return result;
229}
230
Michael W. Hudson7bb466a2002-03-05 13:27:58 +0000231static PyObject *
232structseq_reduce(PyStructSequence* self)
233{
234 PyObject* tup;
Michael W. Hudsonce358e32002-03-06 17:07:49 +0000235 PyObject* dict;
Michael W. Hudson70ffddf2002-03-07 15:13:40 +0000236 PyObject* result;
Michael W. Hudsonce358e32002-03-06 17:07:49 +0000237 long n_fields, n_visible_fields;
Michael W. Hudson7bb466a2002-03-05 13:27:58 +0000238 int i;
239
240 n_fields = REAL_SIZE(self);
Michael W. Hudsonce358e32002-03-06 17:07:49 +0000241 n_visible_fields = VISIBLE_SIZE(self);
242 tup = PyTuple_New(n_visible_fields);
Michael W. Hudson7bb466a2002-03-05 13:27:58 +0000243 if (!tup) {
244 return NULL;
245 }
246
Michael W. Hudsonce358e32002-03-06 17:07:49 +0000247 dict = PyDict_New();
248 if (!dict) {
249 Py_DECREF(tup);
250 return NULL;
251 }
252
253 for (i = 0; i < n_visible_fields; i++) {
Michael W. Hudson7bb466a2002-03-05 13:27:58 +0000254 Py_INCREF(self->ob_item[i]);
255 PyTuple_SET_ITEM(tup, i, self->ob_item[i]);
256 }
257
Michael W. Hudsonce358e32002-03-06 17:07:49 +0000258 for (; i < n_fields; i++) {
259 PyDict_SetItemString(dict, self->ob_type->tp_members[i].name,
260 self->ob_item[i]);
261 }
262
Michael W. Hudson70ffddf2002-03-07 15:13:40 +0000263 result = Py_BuildValue("(O(OO))", self->ob_type, tup, dict);
264
265 Py_DECREF(tup);
266 Py_DECREF(dict);
267
268 return result;
Michael W. Hudson7bb466a2002-03-05 13:27:58 +0000269}
270
Guido van Rossume82f75a2001-10-18 20:47:51 +0000271static PySequenceMethods structseq_as_sequence = {
272 (inquiry)structseq_length,
273 (binaryfunc)structseq_concat, /* sq_concat */
274 (intargfunc)structseq_repeat, /* sq_repeat */
275 (intargfunc)structseq_item, /* sq_item */
276 (intintargfunc)structseq_slice, /* sq_slice */
277 0, /* sq_ass_item */
278 0, /* sq_ass_slice */
279 (objobjproc)structseq_contains, /* sq_contains */
280};
281
Michael W. Hudson7bb466a2002-03-05 13:27:58 +0000282static PyMethodDef structseq_methods[] = {
283 {"__reduce__", (PyCFunction)structseq_reduce,
284 METH_NOARGS, NULL},
285 {NULL, NULL}
286};
287
Guido van Rossume82f75a2001-10-18 20:47:51 +0000288static PyTypeObject _struct_sequence_template = {
289 PyObject_HEAD_INIT(&PyType_Type)
290 0, /* ob_size */
291 NULL, /* tp_name */
292 0, /* tp_basicsize */
293 0, /* tp_itemsize */
294 (destructor)structseq_dealloc, /* tp_dealloc */
295 0, /* tp_print */
296 0, /* tp_getattr */
297 0, /* tp_setattr */
298 0, /* tp_compare */
299 (reprfunc)structseq_repr, /* tp_repr */
300 0, /* tp_as_number */
301 &structseq_as_sequence, /* tp_as_sequence */
302 0, /* tp_as_mapping */
303 (hashfunc)structseq_hash, /* tp_hash */
304 0, /* tp_call */
305 0, /* tp_str */
306 0, /* tp_getattro */
307 0, /* tp_setattro */
308 0, /* tp_as_buffer */
309 Py_TPFLAGS_DEFAULT, /* tp_flags */
310 NULL, /* tp_doc */
311 0, /* tp_traverse */
312 0, /* tp_clear */
313 structseq_richcompare, /* tp_richcompare */
314 0, /* tp_weaklistoffset */
315 0, /* tp_iter */
316 0, /* tp_iternext */
Michael W. Hudson7bb466a2002-03-05 13:27:58 +0000317 structseq_methods, /* tp_methods */
Guido van Rossume82f75a2001-10-18 20:47:51 +0000318 NULL, /* tp_members */
319 0, /* tp_getset */
320 0, /* tp_base */
321 0, /* tp_dict */
322 0, /* tp_descr_get */
323 0, /* tp_descr_set */
324 0, /* tp_dictoffset */
325 0, /* tp_init */
326 0, /* tp_alloc */
327 structseq_new, /* tp_new */
328};
329
330void
331PyStructSequence_InitType(PyTypeObject *type, PyStructSequence_Desc *desc)
332{
333 PyObject *dict;
334 PyMemberDef* members;
335 int n_members, i;
336
337 for (i = 0; desc->fields[i].name != NULL; ++i)
338 ;
339 n_members = i;
340
341 memcpy(type, &_struct_sequence_template, sizeof(PyTypeObject));
342 type->tp_name = desc->name;
343 type->tp_doc = desc->doc;
344 type->tp_basicsize = sizeof(PyStructSequence)+
345 sizeof(PyObject*)*(n_members-1);
346 type->tp_itemsize = 0;
347
348 members = PyMem_NEW(PyMemberDef, n_members+1);
349
350 for (i = 0; i < n_members; ++i) {
351 members[i].name = desc->fields[i].name;
352 members[i].type = T_OBJECT;
353 members[i].offset = offsetof(PyStructSequence, ob_item)
354 + i * sizeof(PyObject*);
355 members[i].flags = READONLY;
356 members[i].doc = desc->fields[i].doc;
357 }
358 members[n_members].name = NULL;
359
360 type->tp_members = members;
361
362 if (PyType_Ready(type) < 0)
363 return;
364 Py_INCREF(type);
365
366 dict = type->tp_dict;
367 PyDict_SetItemString(dict, visible_length_key,
368 PyInt_FromLong((long) desc->n_in_sequence));
369 PyDict_SetItemString(dict, real_length_key,
370 PyInt_FromLong((long) n_members));
Michael W. Hudson7bb466a2002-03-05 13:27:58 +0000371 PyDict_SetItemString(dict, "__safe_for_unpickling__",
372 PyInt_FromLong(1));
Guido van Rossume82f75a2001-10-18 20:47:51 +0000373}