5 * (c) 2005 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of the Python interface to mLib.
12 * mLib/Python is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * mLib/Python is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with mLib/Python; if not, write to the Free Software Foundation,
24 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 /*----- Header files ------------------------------------------------------*/
29 #define PY_SSIZE_T_CLEAN
35 #include <mLib/darray.h>
36 #include <mLib/dstr.h>
42 /*----- Data structures ---------------------------------------------------*/
44 DA_DECL(obj_v
, PyObject
*);
46 typedef struct da_pyobj
{
50 #define DA_PYCHECK(o) PyObject_TypeCheck((o), &da_pytype)
51 #define DA_V(o) (&((da_pyobj *)(o))->v)
53 typedef struct daiter_pyobj
{
58 #define DAITER_DA(obj) (((daiter_pyobj *)(obj))->da)
59 #define DAITER_V(obj) DA_V(DAITER_DA(obj))
60 #define DAITER_I(obj) (((daiter_pyobj *)(obj))->i)
62 static int getseq(PyObject
**pseq
, PyObject
***v
, size_t *n
)
64 PyObject
*seq
= *pseq
;
66 if (!seq
|| seq
== Py_None
) {
70 } else if (DA_PYCHECK(seq
)) {
72 *n
= DA_LEN(DA_V(seq
));
74 } else if ((seq
= PySequence_Fast(seq
, "expected iterable")) == 0)
78 *v
= PySequence_Fast_ITEMS(seq
);
79 *n
= PySequence_Fast_GET_SIZE(seq
);
84 static void range_inc(PyObject
**v
, PyObject
**vl
)
85 { while (v
< vl
) { Py_INCREF(*v
); v
++; } }
86 static void range_dec(PyObject
**v
, PyObject
**vl
)
87 { if (v
) { while (v
< vl
) { Py_DECREF(*v
); v
++; } } }
88 static void range_copy(PyObject
***gv
, PyObject
***gvl
)
90 size_t n
= *gvl
- *gv
;
91 size_t sz
= sizeof(PyObject
*) * n
;
93 if (!n
) { *gv
= *gvl
= 0; return; }
100 static PyObject
*abstract_pynew(PyTypeObject
*ty
,
101 PyObject
*hunoz
, PyObject
*hukairz
)
103 PyErr_SetString(PyExc_TypeError
, "can't instantiate this type");
107 /*----- Iterator ----------------------------------------------------------*/
109 static PyObject
*daiter_pynext(PyObject
*me
)
113 if (DAITER_I(me
) >= DA_LEN(DAITER_V(me
))) return (0);
114 x
= DA(DAITER_V(me
))[DAITER_I(me
)]; DAITER_I(me
)++; RETURN_OBJ(x
);
117 static void daiter_pydealloc(PyObject
*me
)
118 { Py_DECREF(DAITER_DA(me
)); PyObject_DEL(me
); }
120 PyTypeObject daiter_pytype
= {
121 PyObject_HEAD_INIT(0) 0, /* Header */
122 "mLib.ArrayIter", /* @tp_name@ */
123 sizeof(daiter_pyobj
), /* @tp_basicsize@ */
124 0, /* @tp_itemsize@ */
126 daiter_pydealloc
, /* @tp_dealloc@ */
128 0, /* @tp_getattr@ */
129 0, /* @tp_setattr@ */
130 0, /* @tp_compare@ */
132 0, /* @tp_as_number@ */
133 0, /* @tp_as_sequence@ */
134 0, /* @tp_as_mapping@ */
138 0, /* @tp_getattro@ */
139 0, /* @tp_setattro@ */
140 0, /* @tp_as_buffer@ */
141 Py_TPFLAGS_DEFAULT
| /* @tp_flags@ */
147 0, /* @tp_traverse@ */
149 0, /* @tp_richcompare@ */
150 0, /* @tp_weaklistoffset@ */
151 PyObject_SelfIter
, /* @tp_iter@ */
152 daiter_pynext
, /* @tp_iternexr@ */
153 0, /* @tp_methods@ */
154 0, /* @tp_members@ */
158 0, /* @tp_descr_get@ */
159 0, /* @tp_descr_set@ */
160 0, /* @tp_dictoffset@ */
162 PyType_GenericAlloc
, /* @tp_alloc@ */
163 abstract_pynew
, /* @tp_new@ */
168 /*----- Main array code ---------------------------------------------------*/
170 static void da_doinsert(PyObject
*me
, PyObject
**items
, size_t n
,
171 size_t start
, size_t end
)
173 PyObject
**v
, **gv
, **gvl
;
174 obj_v
*da
= DA_V(me
);
179 gv
= v
+ start
; gvl
= v
+ end
; range_copy(&gv
, &gvl
);
180 if (start
< DA_LEN(da
) - end
) {
184 DA_UNSAFE_SLIDE(da
, off
);
185 memmove(v
, v
+ off
, sizeof(PyObject
*) * start
);
188 memmove(v
+ off
, v
, sizeof(PyObject
*) * start
);
189 DA_UNSAFE_UNSLIDE(da
, off
);
195 memmove(v
+ end
+ off
, v
+ end
,
196 sizeof(PyObject
*) * (DA_LEN(da
) - end
));
199 memmove(v
+ end
- off
, v
+ end
,
200 sizeof(PyObject
*) * (DA_LEN(da
) - end
));
201 DA_UNSAFE_SHRINK(da
, off
);
203 DA_UNSAFE_EXTEND(da
, off
);
208 memcpy(v
, items
, sizeof(PyObject
*) * n
);
217 static int da_insert(PyObject
*me
, PyObject
*seq
, int start
, int end
)
222 if (0 > start
|| start
> end
|| end
> DA_LEN(DA_V(me
))) {
223 PyErr_SetString(PyExc_IndexError
, "bad slice");
226 if (getseq(&seq
, &items
, &n
)) return (-1);
227 da_doinsert(me
, items
, n
, start
, end
);
232 static PyObject
*da_new(PyTypeObject
*ty
)
234 da_pyobj
*me
= (da_pyobj
*)ty
->tp_alloc(ty
, 0);
236 return ((PyObject
*)me
);
239 static PyObject
*da_pynew(PyTypeObject
*ty
, PyObject
*arg
, PyObject
*kw
)
240 { return (da_new(ty
)); }
241 static int da_pyinit(PyObject
*me
, PyObject
*arg
, PyObject
*kw
)
244 static char *kwlist
[] = { "sequence", 0 };
246 if (!PyArg_ParseTupleAndKeywords(arg
, kw
, "|O:new", kwlist
, &init
) ||
247 (init
&& da_insert((PyObject
*)me
, init
, 0, 0)))
252 static void da_pydealloc(PyObject
*me
)
255 PyObject
**v
= DA(DA_V(me
));
256 size_t n
= DA_LEN(DA_V(me
));
258 for (i
= 0; i
< n
; i
++) Py_DECREF(v
[i
]);
259 DA_DESTROY(DA_V(me
));
262 static Py_ssize_t
da_pylength(PyObject
*me
)
263 { return (DA_LEN(DA_V(me
))); }
265 static int da_pytraverse(PyObject
*me
, visitproc proc
, void *arg
)
268 PyObject
**v
= DA(DA_V(me
));
269 size_t n
= DA_LEN(DA_V(me
));
272 for (i
= 0; i
< n
; i
++) {
273 if ((err
= proc(v
[i
], arg
)) != 0)
279 static int da_pyclear(PyObject
*me
)
281 range_dec(DA(DA_V(me
)), DA(DA_V(me
)) + DA_LEN(DA_V(me
)));
286 static PyObject
*da_pyconcat(PyObject
*me
, PyObject
*other
)
288 PyObject
*x
= da_new(&da_pytype
);
289 PyObject
**items
= DA(DA_V(me
));
290 size_t n
= DA_LEN(DA_V(me
));
292 da_doinsert(x
, items
, n
, 0, 0);
293 if (da_insert(x
, other
, n
, n
)) {
300 static PyObject
*da_pyrepeat(PyObject
*me
, Py_ssize_t times
)
302 PyObject
*x
= da_new(&da_pytype
);
303 PyObject
**items
= DA(DA_V(me
)), **dest
;
304 size_t n
= DA_LEN(DA_V(me
));
307 DA_ENSURE(DA_V(x
), n
* times
);
308 DA_UNSAFE_EXTEND(DA_V(x
), n
* times
);
309 for (i
= 0, dest
= DA(DA_V(x
)); i
< times
; i
++, dest
+= n
)
310 memcpy(dest
, items
, n
* sizeof(PyObject
*));
311 range_inc(DA(DA_V(x
)), DA(DA_V(x
)) + n
* times
);
315 static PyObject
*da_pygetitem(PyObject
*me
, Py_ssize_t i
)
319 if (i
< 0 || i
>= DA_LEN(DA_V(me
))) {
320 PyErr_SetString(PyExc_IndexError
, "index out of range");
328 static PyObject
*da_pygetslice(PyObject
*me
, Py_ssize_t i
, Py_ssize_t j
)
332 if (i
< 0 || j
< i
|| DA_LEN(DA_V(me
)) < j
) {
333 PyErr_SetString(PyExc_IndexError
, "bad slice");
336 x
= da_new(&da_pytype
);
337 da_doinsert(x
, DA(DA_V(me
)) + i
, j
- i
, 0, 0);
341 static int da_pyputitem(PyObject
*me
, Py_ssize_t i
, PyObject
*x
)
345 if (i
< 0 || i
>= DA_LEN(DA_V(me
))) {
346 PyErr_SetString(PyExc_IndexError
, "index out of range");
349 p
= DA(DA_V(me
)) + i
;
356 static int da_pyputslice(PyObject
*me
, Py_ssize_t i
, Py_ssize_t j
,
358 { return (da_insert(me
, x
, i
, j
)); }
360 static int da_pycontainsp(PyObject
*me
, PyObject
*x
)
362 PyObject
**items
= DA(DA_V(me
));
363 size_t n
= DA_LEN(DA_V(me
));
367 for (i
= 0; i
< n
; i
++) {
368 if (PyObject_Cmp(items
[i
], x
, &rc
))
376 static PyObject
*da_pyrepr(PyObject
*me
)
379 PyObject
*s
, *rc
= 0;
384 dstr_puts(&d
, "Array([");
385 for (i
= 0; i
< DA_LEN(DA_V(me
)); i
++) {
386 if ((s
= PyObject_Repr(DA(DA_V(me
))[i
])) == 0 ||
387 PyString_AsStringAndSize(s
, &p
, &n
)) {
391 if (i
) dstr_puts(&d
, ", ");
396 rc
= PyString_FromStringAndSize(d
.buf
, d
.len
);
402 static PyObject
*da_pyappend(PyObject
*me
, PyObject
*seq
)
404 size_t n
= DA_LEN(DA_V(me
));
405 if (da_insert(me
, seq
, n
, n
)) return (0);
409 static PyObject
*da_pyiprepeat(PyObject
*me
, Py_ssize_t times
)
411 PyObject
**items
, **dest
;
412 size_t n
= DA_LEN(DA_V(me
));
416 PyErr_SetString(PyExc_ValueError
, "multiplier must be nonnegative");
420 items
= DA(DA_V(me
));
421 range_dec(items
, items
+ n
);
426 DA_ENSURE(DA_V(me
), n
* times
);
427 items
= DA(DA_V(me
));
428 for (i
= 0, dest
= items
+ n
; i
< times
; i
++, dest
+= n
)
429 memcpy(dest
, items
, n
* sizeof(PyObject
*));
430 range_inc(items
+ n
, dest
);
431 DA_UNSAFE_EXTEND(DA_V(me
), n
* times
);
435 static PyObject
*da_pyget(PyObject
*me
, PyObject
*index
)
437 if (PySlice_Check(index
)) {
438 Py_ssize_t start
, stop
, step
, len
;
443 if (PySlice_GetIndicesEx((PySliceObject
*)index
, DA_LEN(DA_V(me
)),
444 &start
, &stop
, &step
, &len
))
446 if (step
== 1) return (da_pygetslice(me
, start
, stop
));
447 v
= da_new(&da_pytype
);
448 DA_ENSURE(DA_V(v
), len
);
450 ww
= DA(DA_V(me
)) + start
;
451 DA_UNSAFE_EXTEND(DA_V(v
), len
);
455 vv
++; ww
+= step
; len
--;
457 return ((PyObject
*)v
);
461 if ((i
= PyInt_AsLong(index
)) == -1 && PyErr_Occurred()) return (0);
462 return (da_pygetitem(me
, i
));
466 static int da_pyput(PyObject
*me
, PyObject
*index
, PyObject
*x
)
468 if (PySlice_Check(index
)) {
469 Py_ssize_t start
, stop
, step
, len
;
475 if (PySlice_GetIndicesEx((PySliceObject
*)index
, DA_LEN(DA_V(me
)),
476 &start
, &stop
, &step
, &len
))
478 if (step
== 1) return (da_insert(me
, x
, start
, stop
));
479 if (getseq(&x
, &vv
, &n
)) return (-1);
481 PyErr_SetString(PyExc_ValueError
, "wrong number of items");
485 g
= gg
= xmalloc(len
* sizeof(PyObject
*));
486 ww
= DA(DA_V(me
)) + start
;
488 *gg
++ = *ww
; *ww
= *vv
++;
499 if ((i
= PyInt_AsLong(index
)) == -1 && PyErr_Occurred()) return (-1);
500 return (da_pyputitem(me
, i
, x
));
504 static PyObject
*da_pyiter(PyObject
*me
)
506 daiter_pyobj
*i
= PyObject_NEW(daiter_pyobj
, &daiter_pytype
);
507 i
->da
= me
; Py_INCREF(me
);
509 return ((PyObject
*)i
);
512 static PyObject
*dameth_push(PyObject
*me
, PyObject
*arg
)
516 if (!PyArg_ParseTuple(arg
, "O:push", &x
)) return (0);
518 DA_PUSH(DA_V(me
), x
);
522 static PyObject
*dameth_pop(PyObject
*me
, PyObject
*arg
)
524 PyObject
*x
= Py_None
;
526 if (!PyArg_ParseTuple(arg
, ":pop")) return (0);
528 x
= DA_POP(DA_V(me
));
529 CATCH
switch (exc_type
) {
531 PyErr_SetString(PyExc_ValueError
, "stack underflow");
539 static PyObject
*dameth_unshift(PyObject
*me
, PyObject
*arg
)
543 if (!PyArg_ParseTuple(arg
, "O:unshift", &x
)) return (0);
545 DA_UNSHIFT(DA_V(me
), x
);
549 static PyObject
*dameth_shift(PyObject
*me
, PyObject
*arg
)
551 PyObject
*x
= Py_None
;
553 if (!PyArg_ParseTuple(arg
, ":shift")) return (0);
555 x
= DA_SHIFT(DA_V(me
));
556 CATCH
switch (exc_type
) {
558 PyErr_SetString(PyExc_ValueError
, "stack underflow");
566 static PyObject
*dameth_tidy(PyObject
*me
, PyObject
*arg
)
568 if (!PyArg_ParseTuple(arg
, ":tidy")) return (0);
573 static PyMethodDef da_pymethods
[] = {
574 #define METHNAME(func) dameth_##func
575 METH (push
, "A.push(X): [A, B, ..., W], X -> [A, B, ..., W, X]")
576 METH (pop
, "A.pop() -> X: [A, B, ..., W, X] -> [A, B, ..., W]")
577 METH (unshift
, "A.unshift(X): [A, B, ..., W], X -> [X, A, ..., W]")
578 METH (shift
, "A.shift() -> X: [X, A, ..., W] -> [A, ..., W], X")
579 METH (tidy
, "A.tidy()")
584 static PySequenceMethods da_pysequence
= {
585 da_pylength
, /* @sq_length@ */
586 da_pyconcat
, /* @sq_concat@ */
587 da_pyrepeat
, /* @sq_repeat@ */
588 da_pygetitem
, /* @sq_item@ */
589 da_pygetslice
, /* @sq_slice@ */
590 da_pyputitem
, /* @sq_ass_item@ */
591 da_pyputslice
, /* @sq_ass_slice@ */
592 da_pycontainsp
, /* @sq_contains@ */
593 da_pyappend
, /* @sq_inplace_concat@ */
594 da_pyiprepeat
/* @sq_inplace_repeat@ */
597 static PyMappingMethods da_pymapping
= {
598 da_pylength
, /* @mp_length@ */
599 da_pyget
, /* @mp_subscript@ */
600 da_pyput
/* @mp_ass_subscript@ */
603 PyTypeObject da_pytype
= {
604 PyObject_HEAD_INIT(0) 0, /* Header */
605 "mLib.Array", /* @tp_name@ */
606 sizeof(da_pyobj
), /* @tp_basicsize@ */
607 0, /* @tp_itemsize@ */
609 da_pydealloc
, /* @tp_dealloc@ */
611 0, /* @tp_getattr@ */
612 0, /* @tp_setattr@ */
613 0, /* @tp_compare@ */
614 da_pyrepr
, /* @tp_repr@ */
615 0, /* @tp_as_number@ */
616 &da_pysequence
, /* @tp_as_sequence@ */
617 &da_pymapping
, /* @tp_as_mapping@ */
620 &da_pyrepr
, /* @tp_str@ */
621 0, /* @tp_getattro@ */
622 0, /* @tp_setattro@ */
623 0, /* @tp_as_buffer@ */
624 Py_TPFLAGS_DEFAULT
| /* @tp_flags@ */
628 "Double-ended array type.",
630 da_pytraverse
, /* @tp_traverse@ */
631 da_pyclear
, /* @tp_clear@ */
632 0, /* @tp_richcompare@ */
633 0, /* @tp_weaklistoffset@ */
634 da_pyiter
, /* @tp_iter@ */
635 0, /* @tp_iternext@ */
636 da_pymethods
, /* @tp_methods@ */
637 0, /* @tp_members@ */
641 0, /* @tp_descr_get@ */
642 0, /* @tp_descr_set@ */
643 0, /* @tp_dictoffset@ */
644 da_pyinit
, /* @tp_init@ */
645 PyType_GenericAlloc
, /* @tp_alloc@ */
646 da_pynew
, /* @tp_new@ */
651 /*----- Initialization ----------------------------------------------------*/
653 void da_pysetup(void)
654 { PyType_Ready(&da_pytype
); PyType_Ready(&daiter_pytype
); }
656 /*----- That's all, folks -------------------------------------------------*/