structseq.c 9.46 KB
Newer Older
1 2 3 4 5 6 7 8 9
/* Implementation helper: a struct that looks like a tuple.  See timemodule
   and posixmodule for example uses. */

#include "Python.h"
#include "structmember.h"
#include "structseq.h"

static char visible_length_key[] = "n_sequence_fields";
static char real_length_key[] = "n_fields";
10
static char unnamed_fields_key[] = "n_unnamed_fields";
11

12 13 14 15
/* Fields with this name have only a field index, not a field name. 
   They are only allowed for indices < n_visible_fields. */
char *PyStructSequence_UnnamedField = "unnamed field";

16 17 18 19 20 21 22 23
#define VISIBLE_SIZE(op) ((op)->ob_size)
#define VISIBLE_SIZE_TP(tp) PyInt_AsLong( \
                      PyDict_GetItemString((tp)->tp_dict, visible_length_key))

#define REAL_SIZE_TP(tp) PyInt_AsLong( \
                      PyDict_GetItemString((tp)->tp_dict, real_length_key))
#define REAL_SIZE(op) REAL_SIZE_TP((op)->ob_type)

24 25 26 27
#define UNNAMED_FIELDS_TP(tp) PyInt_AsLong( \
                      PyDict_GetItemString((tp)->tp_dict, unnamed_fields_key))
#define UNNAMED_FIELDS(op) UNNAMED_FIELDS_TP((op)->ob_type)

28 29 30 31 32 33

PyObject *
PyStructSequence_New(PyTypeObject *type)
{
	PyStructSequence *obj;
       
34
	obj = PyObject_New(PyStructSequence, type);
35 36 37 38 39 40 41 42 43 44 45 46 47 48
	obj->ob_size = VISIBLE_SIZE_TP(type);

	return (PyObject*) obj;
}

static void
structseq_dealloc(PyStructSequence *obj)
{
	int i, size;

	size = REAL_SIZE(obj);
	for (i = 0; i < size; ++i) {
		Py_XDECREF(obj->ob_item[i]);
	}
49
	PyObject_Del(obj);
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
}

static int
structseq_length(PyStructSequence *obj)
{
	return VISIBLE_SIZE(obj);
}

static PyObject*
structseq_item(PyStructSequence *obj, int i)
{
	if (i < 0 || i >= VISIBLE_SIZE(obj)) {
		PyErr_SetString(PyExc_IndexError, "tuple index out of range");
		return NULL;
	}
	Py_INCREF(obj->ob_item[i]);
	return obj->ob_item[i];
}

static PyObject*
structseq_slice(PyStructSequence *obj, int low, int high)
{
	PyTupleObject *np;
	int i;

	if (low < 0)
		low = 0;
	if (high > VISIBLE_SIZE(obj))
		high = VISIBLE_SIZE(obj);
	if (high < low)
		high = low;
	np = (PyTupleObject *)PyTuple_New(high-low);
	if (np == NULL)
		return NULL;
	for(i = low; i < high; ++i) {
		PyObject *v = obj->ob_item[i];
		Py_INCREF(v);
87
		PyTuple_SET_ITEM(np, i-low, v);
88 89 90 91 92 93 94 95
	}
	return (PyObject *) np;
}

static PyObject *
structseq_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
	PyObject *arg = NULL;
Michael W. Hudson's avatar
Michael W. Hudson committed
96 97
	PyObject *dict = NULL;
	PyObject *ob;
98
	PyStructSequence *res = NULL;
99
	int len, min_len, max_len, i, n_unnamed_fields;
Michael W. Hudson's avatar
Michael W. Hudson committed
100
	static char *kwlist[] = {"sequence", "dict", 0};
101

Michael W. Hudson's avatar
Michael W. Hudson committed
102 103
	if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:structseq", 
					 kwlist, &arg, &dict))
104 105
		return NULL;

Michael W. Hudson's avatar
Michael W. Hudson committed
106 107 108
	arg = PySequence_Fast(arg, "constructor requires a sequence");

	if (!arg) {				
109 110 111
		return NULL;
	}

Michael W. Hudson's avatar
Michael W. Hudson committed
112 113 114 115 116
	if (dict && !PyDict_Check(dict)) {
		PyErr_Format(PyExc_TypeError, 
			     "%.500s() takes a dict as second arg, if any",
			     type->tp_name);
		Py_DECREF(arg);
117 118 119
		return NULL;
	}

Michael W. Hudson's avatar
Michael W. Hudson committed
120 121 122
	len = PySequence_Fast_GET_SIZE(arg);
	min_len = VISIBLE_SIZE_TP(type);
	max_len = REAL_SIZE_TP(type);
123
	n_unnamed_fields = UNNAMED_FIELDS_TP(type);
Michael W. Hudson's avatar
Michael W. Hudson committed
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151

	if (min_len != max_len) {
		if (len < min_len) {
			PyErr_Format(PyExc_TypeError, 
	       "%.500s() takes an at least %d-sequence (%d-sequence given)",
				     type->tp_name, min_len, len);
			Py_DECREF(arg);
			return NULL;
		}

		if (len > max_len) {
			PyErr_Format(PyExc_TypeError, 
	       "%.500s() takes an at most %d-sequence (%d-sequence given)",
				     type->tp_name, max_len, len);
			Py_DECREF(arg);
			return NULL;
		}
	} 
	else {
		if (len != min_len) {
			PyErr_Format(PyExc_TypeError, 
	       "%.500s() takes a %d-sequence (%d-sequence given)",
				     type->tp_name, min_len, len);
			Py_DECREF(arg);
			return NULL;
		}
	}

152 153
	res = (PyStructSequence*) PyStructSequence_New(type);
	for (i = 0; i < len; ++i) {
Michael W. Hudson's avatar
Michael W. Hudson committed
154 155 156 157 158 159
		PyObject *v = PySequence_Fast_GET_ITEM(arg, i);
		Py_INCREF(v);
		res->ob_item[i] = v;
	}
	for (; i < max_len; ++i) {
		if (dict && (ob = PyDict_GetItemString(
160
			dict, type->tp_members[i-n_unnamed_fields].name))) {
Michael W. Hudson's avatar
Michael W. Hudson committed
161 162 163 164 165 166
		}
		else {
			ob = Py_None;
		}
		Py_INCREF(ob);
		res->ob_item[i] = ob;
167 168
	}
	
Michael W. Hudson's avatar
Michael W. Hudson committed
169
	Py_DECREF(arg);
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
	return (PyObject*) res;
}

static PyObject *
make_tuple(PyStructSequence *obj)
{
	return structseq_slice(obj, 0, VISIBLE_SIZE(obj));
}

static PyObject *
structseq_repr(PyStructSequence *obj)
{
	PyObject *tup, *str;
	tup = make_tuple(obj);
	str = PyObject_Repr(tup);
	Py_DECREF(tup);
	return str;
}

static PyObject *
structseq_concat(PyStructSequence *obj, PyObject *b)
{
	PyObject *tup, *result;
	tup = make_tuple(obj);
	result = PySequence_Concat(tup, b);
	Py_DECREF(tup);
	return result;
}

static PyObject *
structseq_repeat(PyStructSequence *obj, int n)
{
	PyObject *tup, *result;
	tup = make_tuple(obj);
	result = PySequence_Repeat(tup, n);
	Py_DECREF(tup);
	return result;
}

static int
structseq_contains(PyStructSequence *obj, PyObject *o)
{
	PyObject *tup;
	int result;
	tup = make_tuple(obj);
	result = PySequence_Contains(tup, o);
	Py_DECREF(tup);
	return result;
}

static long
structseq_hash(PyObject *obj)
{
	PyObject *tup;
	long result;
	tup = make_tuple((PyStructSequence*) obj);
	result = PyObject_Hash(tup);
	Py_DECREF(tup);
	return result;
}

static PyObject *
structseq_richcompare(PyObject *obj, PyObject *o2, int op)
{
	PyObject *tup, *result;
	tup = make_tuple((PyStructSequence*) obj);
	result = PyObject_RichCompare(tup, o2, op);
	Py_DECREF(tup);
	return result;
}

Michael W. Hudson's avatar
Michael W. Hudson committed
241 242 243 244
static PyObject *
structseq_reduce(PyStructSequence* self)
{
	PyObject* tup;
Michael W. Hudson's avatar
Michael W. Hudson committed
245
	PyObject* dict;
246
	PyObject* result;
247
	long n_fields, n_visible_fields, n_unnamed_fields;
Michael W. Hudson's avatar
Michael W. Hudson committed
248 249 250
	int i;
	
	n_fields = REAL_SIZE(self);
Michael W. Hudson's avatar
Michael W. Hudson committed
251
	n_visible_fields = VISIBLE_SIZE(self);
252
	n_unnamed_fields = UNNAMED_FIELDS(self);
Michael W. Hudson's avatar
Michael W. Hudson committed
253
	tup = PyTuple_New(n_visible_fields);
Michael W. Hudson's avatar
Michael W. Hudson committed
254 255 256 257
	if (!tup) {
		return NULL;
	}

Michael W. Hudson's avatar
Michael W. Hudson committed
258 259 260 261 262 263 264
	dict = PyDict_New();
	if (!dict) {
		Py_DECREF(tup);
		return NULL;
	}

	for (i = 0; i < n_visible_fields; i++) {
Michael W. Hudson's avatar
Michael W. Hudson committed
265 266 267 268
		Py_INCREF(self->ob_item[i]);
		PyTuple_SET_ITEM(tup, i, self->ob_item[i]);
	}
	
Michael W. Hudson's avatar
Michael W. Hudson committed
269
	for (; i < n_fields; i++) {
270 271
		char *n = self->ob_type->tp_members[i-n_unnamed_fields].name;
		PyDict_SetItemString(dict, n,
Michael W. Hudson's avatar
Michael W. Hudson committed
272 273 274
				     self->ob_item[i]);
	}

275 276 277 278 279 280
	result = Py_BuildValue("(O(OO))", self->ob_type, tup, dict);

	Py_DECREF(tup);
	Py_DECREF(dict);

	return result;
Michael W. Hudson's avatar
Michael W. Hudson committed
281 282
}

283 284 285 286 287 288 289 290 291 292 293
static PySequenceMethods structseq_as_sequence = {
	(inquiry)structseq_length,
	(binaryfunc)structseq_concat,           /* sq_concat */
	(intargfunc)structseq_repeat,         	/* sq_repeat */
	(intargfunc)structseq_item,		/* sq_item */
	(intintargfunc)structseq_slice,		/* sq_slice */
	0,					/* sq_ass_item */
	0,					/* sq_ass_slice */
	(objobjproc)structseq_contains,	        /* sq_contains */
};

Michael W. Hudson's avatar
Michael W. Hudson committed
294 295 296 297 298 299
static PyMethodDef structseq_methods[] = {
	{"__reduce__", (PyCFunction)structseq_reduce, 
	 METH_NOARGS, NULL},
	{NULL, NULL}
};

300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
static PyTypeObject _struct_sequence_template = {
	PyObject_HEAD_INIT(&PyType_Type)
	0,					/* ob_size */
	NULL,	                     		/* tp_name */
        0,		                        /* tp_basicsize */
	0,	                      		/* tp_itemsize */
	(destructor)structseq_dealloc,	        /* tp_dealloc */
	0,                        	        /* tp_print */
	0,			 		/* tp_getattr */
	0,					/* tp_setattr */
	0,               			/* tp_compare */
	(reprfunc)structseq_repr,             	/* tp_repr */
	0,					/* tp_as_number */
	&structseq_as_sequence,			/* tp_as_sequence */
	0,					/* tp_as_mapping */
	(hashfunc)structseq_hash,              	/* tp_hash */
	0,              			/* tp_call */
	0,					/* tp_str */
	0,                       		/* tp_getattro */
	0,	                           	/* tp_setattro */
	0,					/* tp_as_buffer */
	Py_TPFLAGS_DEFAULT,                     /* tp_flags */
	NULL,	 		         	/* tp_doc */
	0,					/* tp_traverse */
	0,					/* tp_clear */
	structseq_richcompare,			/* tp_richcompare */
	0,					/* tp_weaklistoffset */
	0,					/* tp_iter */
	0,					/* tp_iternext */
Michael W. Hudson's avatar
Michael W. Hudson committed
329
	structseq_methods,      		/* tp_methods */
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346
        NULL,			             	/* tp_members */
	0,			          	/* tp_getset */
	0,					/* tp_base */
	0,					/* tp_dict */
	0,					/* tp_descr_get */
	0,					/* tp_descr_set */
	0,	                                /* tp_dictoffset */
	0,					/* tp_init */
	0,					/* tp_alloc */
	structseq_new,				/* tp_new */
};

void
PyStructSequence_InitType(PyTypeObject *type, PyStructSequence_Desc *desc)
{
	PyObject *dict;
	PyMemberDef* members;
347
	int n_members, n_unnamed_members, i, k;
348

349
	n_unnamed_members = 0;
350
	for (i = 0; desc->fields[i].name != NULL; ++i)
351
		if (desc->fields[i].name == PyStructSequence_UnnamedField)
352
			n_unnamed_members++;
353 354 355 356 357 358 359 360 361
	n_members = i;

	memcpy(type, &_struct_sequence_template, sizeof(PyTypeObject));
	type->tp_name = desc->name;
	type->tp_doc = desc->doc;
	type->tp_basicsize = sizeof(PyStructSequence)+
		sizeof(PyObject*)*(n_members-1);
	type->tp_itemsize = 0;

362
	members = PyMem_NEW(PyMemberDef, n_members-n_unnamed_members+1);
363
	
364 365 366 367 368 369
	for (i = k = 0; i < n_members; ++i) {
		if (desc->fields[i].name == PyStructSequence_UnnamedField)
			continue;
		members[k].name = desc->fields[i].name;
		members[k].type = T_OBJECT;
		members[k].offset = offsetof(PyStructSequence, ob_item)
370
		  + i * sizeof(PyObject*);
371 372 373
		members[k].flags = READONLY;
		members[k].doc = desc->fields[i].doc;
		k++;
374
	}
375
	members[k].name = NULL;
376 377 378 379 380 381 382 383 384 385 386 387

	type->tp_members = members;

	if (PyType_Ready(type) < 0)
		return;
	Py_INCREF(type);

	dict = type->tp_dict;
	PyDict_SetItemString(dict, visible_length_key, 
		       PyInt_FromLong((long) desc->n_in_sequence));
	PyDict_SetItemString(dict, real_length_key, 
		       PyInt_FromLong((long) n_members));
388 389
	PyDict_SetItemString(dict, unnamed_fields_key, 
		       PyInt_FromLong((long) n_unnamed_members));
Michael W. Hudson's avatar
Michael W. Hudson committed
390 391
	PyDict_SetItemString(dict, "__safe_for_unpickling__", 
		       PyInt_FromLong(1));
392
}