From 20bce5e92b01cd928f26b61be78215117039c561 Mon Sep 17 00:00:00 2001 Message-Id: <20bce5e92b01cd928f26b61be78215117039c561.1714574366.git.mdw@distorted.org.uk> From: Mark Wooding Date: Thu, 13 Oct 2005 17:07:47 +0000 Subject: [PATCH] Initial import. Organization: Straylight/Edgeware From: mdw --- .skelrc | 9 + MANIFEST.in | 3 + Makefile | 26 ++ README | 1 + array.c | 663 +++++++++++++++++++++++++++++++++++++++++++++++ atom-base.c | 213 +++++++++++++++ atom.h | 64 +++++ atom.pyx | 296 +++++++++++++++++++++ codec.pyx.in | 139 ++++++++++ crc32.pyx | 70 +++++ grim.h | 76 ++++++ mLib/__init__.py | 1 + report.pyx | 48 ++++ select.pyx | 634 ++++++++++++++++++++++++++++++++++++++++++++ setup.py | 102 ++++++++ sym.pyx | 267 +++++++++++++++++++ unihash.pyx | 86 ++++++ 17 files changed, 2698 insertions(+) create mode 100644 .skelrc create mode 100644 MANIFEST.in create mode 100644 Makefile create mode 100644 README create mode 100644 array.c create mode 100644 atom-base.c create mode 100644 atom.h create mode 100644 atom.pyx create mode 100644 codec.pyx.in create mode 100644 crc32.pyx create mode 100644 grim.h create mode 100644 mLib/__init__.py create mode 100644 report.pyx create mode 100644 select.pyx create mode 100644 setup.py create mode 100644 sym.pyx create mode 100644 unihash.pyx diff --git a/.skelrc b/.skelrc new file mode 100644 index 0000000..aa186c8 --- /dev/null +++ b/.skelrc @@ -0,0 +1,9 @@ +;;; -*-emacs-lisp-*- + +(setq skel-alist + (append + '((author . "Straylight/Edgeware") + (licence-text . "[[gpl]]") + (full-title . "the Python interface to mLib") + (program . "mLib/Python")) + skel-alist)) diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..b051d6b --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,3 @@ +include atom.pyx crc32.pyx report.pyx select.pyx sym.pyx unihash.pyx +include codec.pyx.in grim.h atom.h atom-base.c array.c Makefile +include debian/rules debian/control debian/changelog debian/copyright diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..53ea323 --- /dev/null +++ b/Makefile @@ -0,0 +1,26 @@ +## Makefile + +PYTHON = python +prefix = /usr/local + +AUTOC = \ + select.c crc32.c atom.c report.c sym.c unihash.c \ + base64.c base32.c hex.c + +GEN = base64.pyx base32.pyx hex.pyx + +all: setup.py + $(PYTHON) setup.py build + +clean: setup.py + $(PYTHON) setup.py clean + rm -rf build + rm -f $(AUTOC) $(GEN) MANIFEST + +dist: setup.py + $(PYTHON) setup.py sdist + +install: setup.py + $(PYTHON) setup.py install --prefix $(prefix) + +.PHONY: all clean dist install diff --git a/README b/README new file mode 100644 index 0000000..8deab3a --- /dev/null +++ b/README @@ -0,0 +1 @@ +Nothing much to say diff --git a/array.c b/array.c new file mode 100644 index 0000000..37850d9 --- /dev/null +++ b/array.c @@ -0,0 +1,663 @@ +/* -*-c-*- + * + * $Id$ + * + * Double-ended arrays + * + * (c) 2005 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the Python interface to mLib. + * + * mLib/Python is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mLib/Python is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with mLib/Python; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Header files ------------------------------------------------------*/ + +#include + +#include + +#include +#include +#include + +#include "grim.h" + +/*----- Data structures ---------------------------------------------------*/ + +DA_DECL(obj_v, PyObject *); + +typedef struct da_pyobj { + PyObject_HEAD + obj_v v; +} da_pyobj; +#define DA_PYCHECK(o) PyObject_TypeCheck((o), &da_pytype) +#define DA_V(o) (&((da_pyobj *)(o))->v) + +typedef struct daiter_pyobj { + PyObject_HEAD + PyObject *da; + size_t i; +} daiter_pyobj; +#define DAITER_DA(obj) (((daiter_pyobj *)(obj))->da) +#define DAITER_V(obj) DA_V(DAITER_DA(obj)) +#define DAITER_I(obj) (((daiter_pyobj *)(obj))->i) + +static PyTypeObject da_pytype, daiter_pytype; + +static int getseq(PyObject **pseq, PyObject ***v, size_t *n) +{ + PyObject *seq = *pseq; + + if (!seq || seq == Py_None) { + *v = 0; + *n = 0; + *pseq = 0; + } else if (DA_PYCHECK(seq)) { + *v = DA(DA_V(seq)); + *n = DA_LEN(DA_V(seq)); + Py_INCREF(seq); + } else if ((seq = PySequence_Fast(seq, "expected iterable")) == 0) + return (-1); + else { + *pseq = seq; + *v = PySequence_Fast_ITEMS(seq); + *n = PySequence_Fast_GET_SIZE(seq); + } + return (0); +} + +static void range_inc(PyObject **v, PyObject **vl) + { while (v < vl) { Py_INCREF(*v); v++; } } +static void range_dec(PyObject **v, PyObject **vl) + { if (v) { while (v < vl) { Py_DECREF(*v); v++; } } } +static void range_copy(PyObject ***gv, PyObject ***gvl) +{ + size_t n = *gvl - *gv; + size_t sz = sizeof(PyObject *) * n; + PyObject **v; + if (!n) { *gv = *gvl = 0; return; } + v = xmalloc(sz); + memcpy(v, *gv, sz); + *gv = v; + *gvl = v + n; +} + +static PyObject *abstract_pynew(PyTypeObject *ty, + PyObject *hunoz, PyObject *hukairz) +{ + PyErr_SetString(PyExc_TypeError, "can't instantiate this type"); + return (0); +} + +/*----- Iterator ----------------------------------------------------------*/ + +static PyObject *daiter_pynext(PyObject *me) +{ + PyObject *x; + + if (DAITER_I(me) >= DA_LEN(DAITER_V(me))) return (0); + x = DA(DAITER_V(me))[DAITER_I(me)]; DAITER_I(me)++; RETURN_OBJ(x); +} + +static void daiter_pydealloc(PyObject *me) + { Py_DECREF(DAITER_DA(me)); PyObject_DEL(me); } + +static PyTypeObject daiter_pytype = { + PyObject_HEAD_INIT(0) 0, /* Header */ + "array.ArrayIter", /* @tp_name@ */ + sizeof(daiter_pyobj), /* @tp_basicsize@ */ + 0, /* @tp_itemsize@ */ + + daiter_pydealloc, /* @tp_dealloc@ */ + 0, /* @tp_print@ */ + 0, /* @tp_getattr@ */ + 0, /* @tp_setattr@ */ + 0, /* @tp_compare@ */ + 0, /* @tp_repr@ */ + 0, /* @tp_as_number@ */ + 0, /* @tp_as_sequence@ */ + 0, /* @tp_as_mapping@ */ + 0, /* @tp_hash@ */ + 0, /* @tp_call@ */ + 0, /* @tp_str@ */ + 0, /* @tp_getattro@ */ + 0, /* @tp_setattro@ */ + 0, /* @tp_as_buffer@ */ + Py_TPFLAGS_DEFAULT | /* @tp_flags@ */ + Py_TPFLAGS_BASETYPE, + + /* @tp_doc@ */ +"Array iterator.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + PyObject_SelfIter, /* @tp_iter@ */ + daiter_pynext, /* @tp_iternexr@ */ + 0, /* @tp_methods@ */ + 0, /* @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@ */ + PyType_GenericAlloc, /* @tp_alloc@ */ + abstract_pynew, /* @tp_new@ */ + 0, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +/*----- Main array code ---------------------------------------------------*/ + +static void da_doinsert(PyObject *me, PyObject **items, size_t n, + size_t start, size_t end) +{ + PyObject **v, **gv, **gvl; + obj_v *da = DA_V(me); + size_t off, ne; + + ne = end - start; + v = DA(da); + gv = v + start; gvl = v + end; range_copy(&gv, &gvl); + if (start < DA_LEN(da) - end) { + if (n > ne) { + off = n - ne; + DA_SHUNT(da, off); + DA_UNSAFE_SLIDE(da, off); + memmove(v, v + off, sizeof(PyObject *) * start); + } else { + off = ne - n; + memmove(v + off, v, sizeof(PyObject *) * start); + DA_UNSAFE_UNSLIDE(da, off); + } + } else { + if (n > ne) { + off = n - ne; + DA_ENSURE(da, off); + memmove(v + end + off, v + end, + sizeof(PyObject *) * (DA_LEN(da) - end)); + } else { + off = ne - n; + memmove(v + end - off, v + end, + sizeof(PyObject *) * (DA_LEN(da) - end)); + DA_UNSAFE_SHRINK(da, off); + } + DA_UNSAFE_EXTEND(da, off); + } + + if (n) { + v = DA(da) + start; + memcpy(v, items, sizeof(PyObject *) * n); + range_inc(v, v + n); + } + if (gv) { + range_dec(gv, gvl); + xfree(gv); + } +} + +static int da_insert(PyObject *me, PyObject *seq, int start, int end) +{ + PyObject **items; + size_t n; + + if (0 > start || start > end || end > DA_LEN(DA_V(me))) { + PyErr_SetString(PyExc_IndexError, "bad slice"); + return (-1); + } + if (getseq(&seq, &items, &n)) return (-1); + da_doinsert(me, items, n, start, end); + Py_XDECREF(seq); + return (0); +} + +static PyObject *da_new(PyTypeObject *ty) +{ + da_pyobj *me = (da_pyobj *)ty->tp_alloc(ty, 0); + DA_CREATE(&me->v); + return ((PyObject *)me); +} + +static PyObject *da_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw) + { return (da_new(ty)); } +static int da_pyinit(PyObject *me, PyObject *arg, PyObject *kw) +{ + PyObject *init = 0; + static char *kwlist[] = { "sequence", 0 }; + + if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O:new", kwlist, &init) || + (init && da_insert((PyObject *)me, init, 0, 0))) + return (-1); + return (0); +} + +static void da_pydealloc(PyObject *me) +{ + size_t i; + PyObject **v = DA(DA_V(me)); + size_t n = DA_LEN(DA_V(me)); + + for (i = 0; i < n; i++) Py_DECREF(v[i]); + DA_DESTROY(DA_V(me)); +} + +static int da_pylength(PyObject *me) + { return (DA_LEN(DA_V(me))); } + +static int da_pytraverse(PyObject *me, visitproc proc, void *arg) +{ + size_t i; + PyObject **v = DA(DA_V(me)); + size_t n = DA_LEN(DA_V(me)); + int err; + + for (i = 0; i < n; i++) { + if ((err = proc(v[i], arg)) != 0) + return (err); + } + return (0); +} + +static int da_pyclear(PyObject *me) +{ + range_dec(DA(DA_V(me)), DA(DA_V(me)) + DA_LEN(DA_V(me))); + DA_RESET(DA_V(me)); + return (0); +} + +static PyObject *da_pyconcat(PyObject *me, PyObject *other) +{ + PyObject *x = da_new(&da_pytype); + PyObject **items = DA(DA_V(me)); + size_t n = DA_LEN(DA_V(me)); + + da_doinsert(x, items, n, 0, 0); + if (da_insert(x, other, n, n)) { + Py_DECREF(x); + return (0); + } + return (x); +} + +static PyObject *da_pyrepeat(PyObject *me, int times) +{ + PyObject *x = da_new(&da_pytype); + PyObject **items = DA(DA_V(me)), **dest; + size_t n = DA_LEN(DA_V(me)); + int i; + + DA_ENSURE(DA_V(x), n * times); + DA_UNSAFE_EXTEND(DA_V(x), n * times); + for (i = 0, dest = DA(DA_V(x)); i < times; i++, dest += n) + memcpy(dest, items, n * sizeof(PyObject *)); + range_inc(DA(DA_V(x)), DA(DA_V(x)) + n * times); + return (x); +} + +static PyObject *da_pygetitem(PyObject *me, int i) +{ + PyObject *o; + + if (i < 0 || i >= DA_LEN(DA_V(me))) { + PyErr_SetString(PyExc_IndexError, "index out of range"); + return (0); + } + o = DA(DA_V(me))[i]; + Py_INCREF(o); + return (o); +} + +static PyObject *da_pygetslice(PyObject *me, int i, int j) +{ + PyObject *x; + + if (i < 0 || j < i || DA_LEN(DA_V(me)) < j) { + PyErr_SetString(PyExc_IndexError, "bad slice"); + return (0); + } + x = da_new(&da_pytype); + da_doinsert(x, DA(DA_V(me)) + i, j - i, 0, 0); + return (x); +} + +static int da_pyputitem(PyObject *me, int i, PyObject *x) +{ + PyObject **p; + + if (i < 0 || i >= DA_LEN(DA_V(me))) { + PyErr_SetString(PyExc_IndexError, "index out of range"); + return (-1); + } + p = DA(DA_V(me)) + i; + Py_DECREF(*p); + *p = x; + Py_INCREF(*p); + return (0); +} + +static int da_pyputslice(PyObject *me, int i, int j, PyObject *x) + { return (da_insert(me, x, i, j)); } + +static int da_pycontainsp(PyObject *me, PyObject *x) +{ + PyObject **items = DA(DA_V(me)); + size_t n = DA_LEN(DA_V(me)); + size_t i; + int rc; + + for (i = 0; i < n; i++) { + if (PyObject_Cmp(items[i], x, &rc)) + return (-1); + if (rc) + return (1); + } + return (0); +} + +static PyObject *da_pyrepr(PyObject *me) +{ + dstr d = DSTR_INIT; + PyObject *s, *rc = 0; + char *p; + int n; + size_t i; + + dstr_puts(&d, "Array(["); + for (i = 0; i < DA_LEN(DA_V(me)); i++) { + if ((s = PyObject_Repr(DA(DA_V(me))[i])) == 0 || + PyString_AsStringAndSize(s, &p, &n)) { + Py_XDECREF(s); + goto done; + } + if (i) dstr_puts(&d, ", "); + dstr_putm(&d, p, n); + Py_DECREF(s); + } + dstr_puts(&d, "])"); + rc = PyString_FromStringAndSize(d.buf, d.len); +done: + dstr_destroy(&d); + return (rc); +} + +static PyObject *da_pyappend(PyObject *me, PyObject *seq) +{ + size_t n = DA_LEN(DA_V(me)); + if (da_insert(me, seq, n, n)) return (0); + RETURN_ME; +} + +static PyObject *da_pyiprepeat(PyObject *me, int times) +{ + PyObject **items, **dest; + size_t n = DA_LEN(DA_V(me)); + int i; + + if (times < 0) { + PyErr_SetString(PyExc_ValueError, "multiplier must be nonnegative"); + return (0); + } + if (!times) { + items = DA(DA_V(me)); + range_dec(items, items + n); + DA_RESET(DA_V(me)); + RETURN_ME; + } + times--; + DA_ENSURE(DA_V(me), n * times); + items = DA(DA_V(me)); + for (i = 0, dest = items + n; i < times; i++, dest += n) + memcpy(dest, items, n * sizeof(PyObject *)); + range_inc(items + n, dest); + DA_UNSAFE_EXTEND(DA_V(me), n * times); + RETURN_ME; +} + +static PyObject *da_pyget(PyObject *me, PyObject *index) +{ + if (PySlice_Check(index)) { + int start, stop, step, len; + PyObject *v; + PyObject **ww; + PyObject **vv; + + if (PySlice_GetIndicesEx((PySliceObject *)index, DA_LEN(DA_V(me)), + &start, &stop, &step, &len)) + return (0); + if (step == 1) return (da_pygetslice(me, start, stop)); + v = da_new(&da_pytype); + DA_ENSURE(DA_V(v), len); + vv = DA(DA_V(v)); + ww = DA(DA_V(me)) + start; + DA_UNSAFE_EXTEND(DA_V(v), len); + while (len) { + *vv = *ww; + Py_INCREF(*vv); + vv++; ww += step; len--; + } + return ((PyObject *)v); + } else { + int i; + + if ((i = PyInt_AsLong(index)) == -1 && PyErr_Occurred()) return (0); + return (da_pygetitem(me, i)); + } +} + +static int da_pyput(PyObject *me, PyObject *index, PyObject *x) +{ + if (PySlice_Check(index)) { + int start, stop, step, len; + size_t n; + PyObject **ww; + PyObject **vv; + PyObject **g, **gg; + + if (PySlice_GetIndicesEx((PySliceObject *)index, DA_LEN(DA_V(me)), + &start, &stop, &step, &len)) + return (-1); + if (step == 1) return (da_insert(me, x, start, stop)); + if (getseq(&x, &vv, &n)) return (-1); + if (n != len) { + PyErr_SetString(PyExc_ValueError, "wrong number of items"); + Py_XDECREF(x); + return (-1); + } + g = gg = xmalloc(len * sizeof(PyObject *)); + ww = DA(DA_V(me)) + start; + while (len) { + *gg++ = *ww; *ww = *vv++; + Py_INCREF(*ww); + ww += step; len--; + } + range_dec(g, gg); + xfree(g); + Py_XDECREF(x); + return (0); + } else { + int i; + + if ((i = PyInt_AsLong(index)) == -1 && PyErr_Occurred()) return (-1); + return (da_pyputitem(me, i, x)); + } +} + +static PyObject *da_pyiter(PyObject *me) +{ + daiter_pyobj *i = PyObject_NEW(daiter_pyobj, &daiter_pytype); + i->da = me; Py_INCREF(me); + i->i = 0; + return ((PyObject *)i); +} + +static PyObject *dameth_push(PyObject *me, PyObject *arg) +{ + PyObject *x; + + if (!PyArg_ParseTuple(arg, "O:push", &x)) return (0); + Py_INCREF(x); + DA_PUSH(DA_V(me), x); + RETURN_ME; +} + +static PyObject *dameth_pop(PyObject *me, PyObject *arg) +{ + PyObject *x; + + if (!PyArg_ParseTuple(arg, ":pop")) return (0); + TRY + x = DA_POP(DA_V(me)); + CATCH switch (exc_type) { + case DAEXC_UFLOW: + PyErr_SetString(PyExc_ValueError, "stack underflow"); + return (0); + default: + RETHROW; + } END_TRY; + RETURN_OBJ(x); +} + +static PyObject *dameth_unshift(PyObject *me, PyObject *arg) +{ + PyObject *x; + + if (!PyArg_ParseTuple(arg, "O:unshift", &x)) return (0); + Py_INCREF(x); + DA_UNSHIFT(DA_V(me), x); + RETURN_ME; +} + +static PyObject *dameth_shift(PyObject *me, PyObject *arg) +{ + PyObject *x; + + if (!PyArg_ParseTuple(arg, ":shift")) return (0); + TRY + x = DA_SHIFT(DA_V(me)); + CATCH switch (exc_type) { + case DAEXC_UFLOW: + PyErr_SetString(PyExc_ValueError, "stack underflow"); + return (0); + default: + RETHROW; + } END_TRY; + RETURN_OBJ(x); +} + +static PyObject *dameth_tidy(PyObject *me, PyObject *arg) +{ + if (!PyArg_ParseTuple(arg, ":tidy")) return (0); + DA_TIDY(DA_V(me)); + RETURN_ME; +} + +static PyMethodDef da_pymethods[] = { +#define METHNAME(func) dameth_##func + METH (push, "A.push(X): [A, B, ..., W], X -> [A, B, ..., W, X]") + METH (pop, "A.pop() -> X: [A, B, ..., W, X] -> [A, B, ..., W]") + METH (unshift, "A.unshift(X): [A, B, ..., W], X -> [X, A, ..., W]") + METH (shift, "A.shift() -> X: [X, A, ..., W] -> [A, ..., W], X") + METH (tidy, "A.tidy()") +#undef METHNAME + { 0 } +}; + +static PySequenceMethods da_pysequence = { + da_pylength, /* @sq_length@ */ + da_pyconcat, /* @sq_concat@ */ + da_pyrepeat, /* @sq_repeat@ */ + da_pygetitem, /* @sq_item@ */ + da_pygetslice, /* @sq_slice@ */ + da_pyputitem, /* @sq_ass_item@ */ + da_pyputslice, /* @sq_ass_slice@ */ + da_pycontainsp, /* @sq_contains@ */ + da_pyappend, /* @sq_inplace_concat@ */ + da_pyiprepeat /* @sq_inplace_repeat@ */ +}; + +static PyMappingMethods da_pymapping = { + da_pylength, /* @mp_length@ */ + da_pyget, /* @mp_subscript@ */ + da_pyput /* @mp_ass_subscript@ */ +}; + +static PyTypeObject da_pytype = { + PyObject_HEAD_INIT(0) 0, /* Header */ + "array.Array", /* @tp_name@ */ + sizeof(da_pyobj), /* @tp_basicsize@ */ + 0, /* @tp_itemsize@ */ + + da_pydealloc, /* @tp_dealloc@ */ + 0, /* @tp_print@ */ + 0, /* @tp_getattr@ */ + 0, /* @tp_setattr@ */ + 0, /* @tp_compare@ */ + da_pyrepr, /* @tp_repr@ */ + 0, /* @tp_as_number@ */ + &da_pysequence, /* @tp_as_sequence@ */ + &da_pymapping, /* @tp_as_mapping@ */ + 0, /* @tp_hash@ */ + 0, /* @tp_call@ */ + &da_pyrepr, /* @tp_str@ */ + 0, /* @tp_getattro@ */ + 0, /* @tp_setattro@ */ + 0, /* @tp_as_buffer@ */ + Py_TPFLAGS_DEFAULT | /* @tp_flags@ */ + Py_TPFLAGS_HAVE_GC, + + /* @tp_doc@ */ +"Double-ended array type.", + + da_pytraverse, /* @tp_traverse@ */ + da_pyclear, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + da_pyiter, /* @tp_iter@ */ + 0, /* @tp_iternext@ */ + da_pymethods, /* @tp_methods@ */ + 0, /* @tp_members@ */ + 0, /* @tp_getset@ */ + 0, /* @tp_base@ */ + 0, /* @tp_dict@ */ + 0, /* @tp_descr_get@ */ + 0, /* @tp_descr_set@ */ + 0, /* @tp_dictoffset@ */ + da_pyinit, /* @tp_init@ */ + PyType_GenericAlloc, /* @tp_alloc@ */ + da_pynew, /* @tp_new@ */ + 0, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +/*----- Initialization ----------------------------------------------------*/ + +static PyMethodDef emptymethods[] = { { 0 } }; + +void initarray(void) +{ + PyObject *mod = Py_InitModule("array", emptymethods); + PyType_Ready(&da_pytype); PyType_Ready(&daiter_pytype); + PyModule_AddObject(mod, "Array", (PyObject *)&da_pytype); + PyModule_AddObject(mod, "ArrayIter", (PyObject *)&daiter_pytype); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/atom-base.c b/atom-base.c new file mode 100644 index 0000000..44b915b --- /dev/null +++ b/atom-base.c @@ -0,0 +1,213 @@ +/* -*-c-*- + * + * $Id$ + * + * Atom stuff + * + * (c) 2005 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the Python interface to mLib. + * + * mLib/Python is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mLib/Python is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with mLib/Python; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Header files ------------------------------------------------------*/ + +#include + +#include +#include +#include + +#include "atom.h" +#include "grim.h" + +/*----- Data structures ---------------------------------------------------*/ + +static PyTypeObject atom_pytype; + +typedef struct entry { + assoc_base _b; + PyObject *a; +} entry; + +/*----- Static variables --------------------------------------------------*/ + +static assoc_table obarray; + +/*----- Main code ---------------------------------------------------------*/ + +PyObject *atom_pywrap(atom *a) +{ + entry *e; + unsigned f = 0; + + e = assoc_find(&obarray, a, sizeof(entry), &f); + if (!f) { + atom_pyobj *ao = PyObject_NEW(atom_pyobj, &atom_pytype); + ao->a = a; + e->a = (PyObject *)ao; + } + RETURN_OBJ(e->a); +} + +PyObject *atom_pyintern(PyObject *x) +{ + atom *a; + const void *p; + int n; + + if (ATOM_PYCHECK(x)) + RETURN_OBJ(x); + if (x == Py_None) + a = atom_gensym(ATOM_GLOBAL); + else { + if (PyObject_AsReadBuffer(x, &p, &n)) return (0); + a = atom_nintern(ATOM_GLOBAL, p, n); + } + return (atom_pywrap(a)); +} + +static void atom_pydealloc(PyObject *me) + { fprintf(stderr, "ouch! freeing atom\n"); abort(); } + +static PyObject *atom_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw) +{ + PyObject *name; + static char *kwlist[] = { "name", 0 }; + + if (!PyArg_ParseTupleAndKeywords(arg, kw, "O:new", kwlist, &name)) + return (0); + return (atom_pyintern(name)); +} + +static PyObject *aget_name(PyObject *me, void *hunoz) +{ + return (PyString_FromStringAndSize(ATOM_NAME(ATOM_A(me)), + ATOM_LEN(ATOM_A(me)))); +} + +static PyObject *aget_internedp(PyObject *me, void *hunoz) +{ + PyObject *rc = (ATOM_A(me)->f & ATOMF_GENSYM) ? Py_False : Py_True; + RETURN_OBJ(rc); +} + +static PyGetSetDef atom_pygetset[] = { +#define GETSETNAME(op, name) a##op##_##name + GET (name, "A.name -> NAME") + GET (internedp, "A.internedp -> BOOL") +#undef GETSETNAME + { 0 } +}; + +static PyObject *atom_pyrichcompare(PyObject *x, PyObject *y, int op) +{ + PyObject *rc = 0; + + switch (op) { + case Py_EQ: rc = (x == y) ? Py_True : Py_False; break; + case Py_NE: rc = (x != y) ? Py_True : Py_False; break; + default: + PyErr_SetString(PyExc_TypeError, "atoms are unordered"); + return (0); + } + RETURN_OBJ(rc); +} + +static PyObject *atom_pyrepr(PyObject *me) +{ + PyObject *s, *sr = 0; + PyObject *rc = 0; + char *p; + int n; + dstr d = DSTR_INIT; + + if ((s = aget_name(me, 0)) == 0 || + (sr = PyObject_Repr(s)) == 0 || + PyString_AsStringAndSize(sr, &p, &n)) + goto done; + dstr_puts(&d, "Atom("); + dstr_putm(&d, p, n); + dstr_puts(&d, ")"); + rc = PyString_FromStringAndSize(d.buf, d.len); +done: + Py_XDECREF(s); + Py_XDECREF(sr); + dstr_destroy(&d); + return (rc); +} + +static long atom_pyhash(PyObject *me) + { long h = ATOM_HASH(ATOM_A(me)); if (h == -1) h = -2; return (h); } + +static PyTypeObject atom_pytype = { + PyObject_HEAD_INIT(0) 0, /* Header */ + "atom.Atom", /* @tp_name@ */ + sizeof(atom_pyobj), /* @tp_basicsize@ */ + 0, /* @tp_itemsize@ */ + + atom_pydealloc, /* @tp_dealloc@ */ + 0, /* @tp_print@ */ + 0, /* @tp_getattr@ */ + 0, /* @tp_setattr@ */ + 0, /* @tp_compare@ */ + atom_pyrepr, /* @tp_repr@ */ + 0, /* @tp_as_number@ */ + 0, /* @tp_as_sequence@ */ + 0, /* @tp_as_mapping@ */ + atom_pyhash, /* @tp_hash@ */ + 0, /* @tp_call@ */ + atom_pyrepr, /* @tp_str@ */ + 0, /* @tp_getattro@ */ + 0, /* @tp_setattro@ */ + 0, /* @tp_as_buffer@ */ + Py_TPFLAGS_DEFAULT, /* @tp_flags@ */ + + /* @tp_doc@ */ +"Atom.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + atom_pyrichcompare, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternexr@ */ + 0, /* @tp_methods@ */ + 0, /* @tp_members@ */ + atom_pygetset, /* @tp_getset@ */ + 0, /* @tp_base@ */ + 0, /* @tp_dict@ */ + 0, /* @tp_descr_get@ */ + 0, /* @tp_descr_set@ */ + 0, /* @tp_dictoffset@ */ + 0, /* @tp_init@ */ + PyType_GenericAlloc, /* @tp_alloc@ */ + atom_pynew, /* @tp_new@ */ + 0, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +PyObject *atom_pystartup(void) +{ + assoc_create(&obarray); + PyType_Ready(&atom_pytype); + return ((PyObject *)&atom_pytype); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/atom.h b/atom.h new file mode 100644 index 0000000..8a2ff31 --- /dev/null +++ b/atom.h @@ -0,0 +1,64 @@ +/* -*-c-*- + * + * $Id$ + * + * Atom stuff + * + * (c) 2005 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the Python interface to mLib. + * + * mLib/Python is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mLib/Python is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with mLib/Python; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef ATOM_H +#define ATOM_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include + +#include +#include + +/*----- Data structures ---------------------------------------------------*/ + +typedef struct atom_pyobj { + PyObject_HEAD + atom *a; +} atom_pyobj; +#define ATOM_PYCHECK(obj) PyObject_TypeCheck(obj, &atom_pytype) +#define ATOM_A(obj) (((atom_pyobj *)(obj))->a) + +/*----- Functions provided ------------------------------------------------*/ + +extern PyObject *atom_pywrap(atom *); +extern PyObject *atom_pyintern(PyObject *); +extern PyObject *atom_pystartup(void); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/atom.pyx b/atom.pyx new file mode 100644 index 0000000..8fac74c --- /dev/null +++ b/atom.pyx @@ -0,0 +1,296 @@ +# -*-pyrex-*- +# +# $Id$ +# +# Atom tables +# +# (c) 2005 Straylight/Edgeware +# + +#----- Licensing notice ----------------------------------------------------- +# +# This file is part of the Python interface to mLib. +# +# mLib/Python is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# mLib/Python is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with mLib/Python; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +cdef extern from 'atom.h': + ctypedef struct atom: + pass + ctypedef struct atom_iter: + pass + ctypedef struct atom_table: + pass + atom_table *ATOM_GLOBAL + void atom_mkiter(atom_iter *i, atom_table *t) + atom *atom_next(atom_iter *) + atom_pystartup() + atom_pywrap(atom *a) + atom_pyintern(obj) + atom *ATOM_A(obj) + +cdef extern from 'mLib/assoc.h': + ctypedef struct assoc_table: + pass + ctypedef struct assoc_base: + pass + ctypedef struct assoc_iter: + pass + void assoc_create(assoc_table *t) + void assoc_destroy(assoc_table *t) + void *assoc_find(assoc_table *t, atom *a, int sz, unsigned *f) + void assoc_remove(assoc_table *t, void *b) + atom *ASSOC_ATOM(void *b) + void assoc_mkiter(assoc_iter *i, assoc_table *t) + void *assoc_next(assoc_iter *i) + +cdef extern from 'grim.h': + int PSIZEOF(void *p) + +cdef extern from 'Python.h': + int PyObject_AsReadBuffer(obj, void **buf, int *len) except -1 + PyString_FromStringAndSize(char *p, int n) + ctypedef struct PyObject: + pass + void Py_INCREF(PyObject *obj) + void Py_DECREF(PyObject *obj) + +cdef class AtomIter: + cdef atom_iter _i + def __new__(me): + atom_mkiter(&me._i, ATOM_GLOBAL) + def __next__(me): + cdef atom *a + a = atom_next(&me._i) + if not a: + raise StopIteration + return atom_pywrap(a) + def __iter__(me): + return me + +def atoms(): + return AtomIter() + +cdef struct entry: + assoc_base _b + PyObject *v + +cdef entry *_find(assoc_table *t, object key, unsigned *f) except ?NULL: + cdef void *p + cdef int n + cdef entry *e + cdef atom *a + a = ATOM_A(atom_pyintern(key)) + if f: + f[0] = 0 + e = assoc_find(t, a, PSIZEOF(e), f) + if not f[0]: + e.v = NULL + else: + e = assoc_find(t, a, 0, NULL) + return e + +cdef _key(entry *e): + return atom_pywrap(ASSOC_ATOM(e)) + +cdef _eget(entry *e): + Py_INCREF(e.v) + return e.v + +cdef void _eset(entry *e, v): + if e.v: + Py_DECREF(e.v) + e.v = v + Py_INCREF(e.v) + +cdef void _edel(assoc_table *t, entry *e): + if e.v: + Py_DECREF(e.v) + assoc_remove(t, e) + +cdef class Table: + cdef assoc_table _t + def __new__(me, *hunoz, **hukairz): + assoc_create(&me._t) + def __init__(me, stuff = None, **kw): + me.update(stuff, **kw) + def __getitem__(me, key): + cdef entry *e + e = _find(&me._t, key, NULL) + if not e: + raise KeyError, key + return _eget(e) + def __setitem__(me, key, value): + cdef unsigned f + _eset(_find(&me._t, key, &f), value) + def __delitem__(me, key): + cdef entry *e + cdef unsigned f + e = _find(&me._t, key, &f) + if not e: + raise KeyError, key + _edel(&me._t, e) + def get(me, key, default = None): + cdef entry *e + e = _find(&me._t, key, NULL) + if not e: + return default + return _eget(e) + def setdefault(me, key, default = None): + cdef entry *e + cdef unsigned f + e = _find(&me._t, key, &f) + if f: + return _eget(e) + else: + _eset(e, default) + return default + def pop(me, key, default = None): + cdef entry *e + e = _find(&me._t, key, NULL) + if not e: + return default + rc = _eget(e) + _edel(&me._t, e) + return rc + def popitem(me): + cdef entry *e + cdef assoc_iter i + assoc_mkiter(&i, &me._t) + e = assoc_next(&i) + if not e: + raise ValueError, 'popitem(): table is empty' + return _key(e), _eget(e) + def keys(me): + cdef assoc_iter i + cdef entry *e + l = [] + assoc_mkiter(&i, &me._t) + while 1: + e = assoc_next(&i) + if not e: + break + l.append(_key(e)) + return l + def values(me): + cdef assoc_iter i + cdef entry *e + l = [] + assoc_mkiter(&i, &me._t) + while 1: + e = assoc_next(&i) + if not e: + break + l.append(_eget(e)) + return l + def items(me): + cdef assoc_iter i + cdef entry *e + l = [] + assoc_mkiter(&i, &me._t) + while 1: + e = assoc_next(&i) + if not e: + break + l.append((_key(e), _eget(e))) + return l + def clear(me): + cdef assoc_iter i + cdef entry *e + assoc_mkiter(&i, &me._t) + while 1: + e = assoc_next(&i) + if not e: + break + _edel(&me._t, e) + return me + def __dealloc__(me): + cdef assoc_iter i + cdef entry *e + assoc_mkiter(&i, &me._t) + while 1: + e = assoc_next(&i) + if not e: + break + _edel(&me._t, e) + assoc_destroy(&me._t) + def iterkeys(me): + return KeyIter(me) + def __iter__(me): + return KeyIter(me) + def itervalues(me): + return ValueIter(me) + def iteritems(me): + return ItemIter(me) + def update(me, stuff = None, **kw): + if stuff is None: + pass + elif hasattr(stuff, 'keys'): + for k in stuff: + me[k] = stuff[k] + else: + for k, v in stuff: + me[k] = v + for k, v in kw.iteritems(): + me[k] = kw[k] + return me + +cdef class KeyIter: + cdef Table _t + cdef assoc_iter _i + def __new__(me, Table t): + me._t = t + assoc_mkiter(&me._i, &t._t) + def __iter__(me): + return me + def __next__(me): + cdef entry *e + e = assoc_next(&me._i) + if not e: + raise StopIteration + return _key(e) + +cdef class ValueIter: + cdef Table _t + cdef assoc_iter _i + def __new__(me, Table t): + me._t = t + assoc_mkiter(&me._i, &t._t) + def __iter__(me): + return me + def __next__(me): + cdef entry *e + e = assoc_next(&me._i) + if not e: + raise StopIteration + return _eget(e) + +cdef class ItemIter: + cdef Table _t + cdef assoc_iter _i + def __new__(me, Table t): + me._t = t + assoc_mkiter(&me._i, &t._t) + def __iter__(me): + return me + def __next__(me): + cdef entry *e + e = assoc_next(&me._i) + if not e: + raise StopIteration + return _key(e), _eget(e) + +Atom = atom_pystartup() + +#----- That's all, folks ---------------------------------------------------- diff --git a/codec.pyx.in b/codec.pyx.in new file mode 100644 index 0000000..60d9714 --- /dev/null +++ b/codec.pyx.in @@ -0,0 +1,139 @@ +# -*-pyrex-*- +# +# $Id$ +# +# Generic encoder/decoder +# +# (c) 2005 Straylight/Edgeware +# + +#----- Licensing notice ----------------------------------------------------- +# +# This file is part of the Python interface to mLib. +# +# mLib/Python is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# mLib/Python is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with mLib/Python; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#----- External dependencies ------------------------------------------------ + +cdef extern from 'stddef.h': + ctypedef int size_t + +cdef extern from 'mLib/dstr.h': + ctypedef struct dstr: + char *buf + int len + void DCREATE(dstr *d) + void dstr_destroy(dstr *d) + +cdef extern from 'mLib/alloc.h': + char *xstrdup(char *p) + void xfree(void *p) + +cdef extern from 'mLib/%PREFIX%.h': + ctypedef struct %PREFIX%_ctx: + char *indent + int maxline + void %PREFIX%_init(%PREFIX%_ctx *b) + void %PREFIX%_encode(%PREFIX%_ctx *b, void *p, size_t sz, dstr *d) + void %PREFIX%_decode(%PREFIX%_ctx *b, void *p, size_t sz, dstr *d) + +cdef extern from 'Python.h': + int PyObject_AsReadBuffer(obj, void **buf, int *len) except -1 + object PyString_FromStringAndSize(char *p, int len) + +cdef class Encode: + cdef %PREFIX%_ctx ctx + def __new__(me, *hunoz, **hukairz): + %PREFIX%_init(&me.ctx) + me.ctx.indent = NULL + def __init__(me, indent = '\n', maxline = 72): + if me.ctx.indent: + xfree(me.ctx.indent) + me.ctx.indent = xstrdup(indent) + me.ctx.maxline = maxline + def __dealloc__(me): + if me.ctx.indent: + xfree(me.ctx.indent) + property indent: + def __get__(me): + return me.ctx.indent + def __set__(me, indent): + if me.ctx.indent: + xfree(me.ctx.indent) + me.ctx.indent = xstrdup(indent) + property maxline: + def __get__(me): + return me.ctx.maxline + def __set__(me, maxline): + me.ctx.maxline = maxline + def encode(me, text): + cdef void *p + cdef int len + cdef dstr d + DCREATE(&d) + try: + PyObject_AsReadBuffer(text, &p, &len) + %PREFIX%_encode(&me.ctx, p, len, &d) + rc = PyString_FromStringAndSize(d.buf, d.len) + finally: + dstr_destroy(&d) + return rc + def done(me): + cdef dstr d + DCREATE(&d) + try: + %PREFIX%_encode(&me.ctx, NULL, 0, &d) + rc = PyString_FromStringAndSize(d.buf, d.len) + finally: + dstr_destroy(&d) + return rc + +def encode(text, *arg, **kw): + e = Encode(*arg, **kw) + return e.encode(text) + e.done() + +cdef class Decode: + cdef %PREFIX%_ctx ctx + def __new__(me, *hunoz, **hukairz): + %PREFIX%_init(&me.ctx) + me.ctx.indent = NULL + def decode(me, text): + cdef void *p + cdef int len + cdef dstr d + DCREATE(&d) + try: + PyObject_AsReadBuffer(text, &p, &len) + %PREFIX%_decode(&me.ctx, p, len, &d) + rc = PyString_FromStringAndSize(d.buf, d.len) + finally: + dstr_destroy(&d) + return rc + def done(me): + cdef dstr d + DCREATE(&d) + try: + %PREFIX%_decode(&me.ctx, NULL, 0, &d) + rc = PyString_FromStringAndSize(d.buf, d.len) + finally: + dstr_destroy(&d) + return rc + +def decode(text, *arg, **kw): + d = Decode(*arg, **kw) + return e.decode(text) + d.done() + +#----- That's all, folks ---------------------------------------------------- + diff --git a/crc32.pyx b/crc32.pyx new file mode 100644 index 0000000..d5e0239 --- /dev/null +++ b/crc32.pyx @@ -0,0 +1,70 @@ +# -*-pyrex-*- +# +# $Id$ +# +# Cyclic redundancy checker +# +# (c) 2005 Straylight/Edgeware +# + +#----- Licensing notice ----------------------------------------------------- +# +# This file is part of the Python interface to mLib. +# +# mLib/Python is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# mLib/Python is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with mLib/Python; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +cdef extern from 'mLib/crc32.h': + ctypedef int uint32 + uint32 c_crc32 "crc32" (uint32 a, void *p, int sz) + +cdef extern from 'limits.h': + enum: + LONG_MAX + +cdef extern from 'Python.h': + int PyObject_AsReadBuffer(obj, void **buf, int *len) except -1 + PyInt_FromLong(long i) + PyLong_FromUnsignedLong(unsigned long i) + +cdef _u32(uint32 x): + if x < LONG_MAX: + return PyInt_FromLong(x) + else: + return PyLong_FromUnsignedLong(x) + +cdef class CRC32: + cdef uint32 _a + def __new__(me, *hunoz, **hukairz): + me._a = 0 + def __init__(me): + pass + def chunk(me, data): + cdef void *p + cdef int n + PyObject_AsReadBuffer(data, &p, &n) + me._a = c_crc32(me._a, p, n) + return me + def done(me): + return _u32(me._a) + +def crc32(data): + cdef void *p + cdef int n + cdef uint32 c + PyObject_AsReadBuffer(data, &p, &n) + c = c_crc32(0, p, n) + return _u32(c) + +#----- That's all, folks ---------------------------------------------------- diff --git a/grim.h b/grim.h new file mode 100644 index 0000000..d7ecbe2 --- /dev/null +++ b/grim.h @@ -0,0 +1,76 @@ +/* -*-c-*- + * + * $Id$ + * + * Grim hacks to support the Pyrex code + * + * (c) 2005 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the Python interface to mLib. + * + * mLib/Python is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mLib/Python is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with mLib/Python; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef GRIM_H +#define GRIM_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include + +/*----- Functions provided ------------------------------------------------*/ + +#define PSIZEOF(x) sizeof(*x) + +/*----- Utilities ---------------------------------------------------------*/ + +#define RETURN_OBJ(obj) do { Py_INCREF(obj); return (obj); } while (0) +#define RETURN_ME RETURN_OBJ(me) +#define RETURN_NONE RETURN_OBJ(Py_None) +#define RETURN_NOTIMPL RETURN_OBJ(Py_NotImplemented) + +#define METH(func, doc) \ + { #func, METHNAME(func), METH_VARARGS, doc }, +#define KWMETH(func, doc) \ + { #func, (PyCFunction)METHNAME(func), \ + METH_VARARGS | METH_KEYWORDS, doc }, + +#define GET(func, doc) \ + { #func, GETSETNAME(get, func), 0, doc }, +#define GETSET(func, doc) \ + { #func, GETSETNAME(get, func), GETSETNAME(set, func), doc }, + +#ifndef PySequence_Fast_ITEMS +# define PySequence_Fast_ITEMS(o) \ + (PyList_Check(o) ? &PyList_GET_ITEM(o, 0) : &PyTuple_GET_ITEM(o, 0)) +#endif + +#define FREEOBJ(obj) \ + (((PyObject *)(obj))->ob_type->tp_free((PyObject *)(obj))) + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/mLib/__init__.py b/mLib/__init__.py new file mode 100644 index 0000000..2ae2839 --- /dev/null +++ b/mLib/__init__.py @@ -0,0 +1 @@ +pass diff --git a/report.pyx b/report.pyx new file mode 100644 index 0000000..e3749b4 --- /dev/null +++ b/report.pyx @@ -0,0 +1,48 @@ +# -*-pyrex-*- +# +# $Id$ +# +# Error reporting and stuff +# +# (c) 2005 Straylight/Edgeware +# + +#----- Licensing notice ----------------------------------------------------- +# +# This file is part of the Python interface to mLib. +# +# mLib/Python is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# mLib/Python is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with mLib/Python; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +cdef extern from 'mLib/quis.h': + void _ego "ego"(char *prog) + char *_quis "quis"() + +cdef extern from 'mLib/report.h': + void _moan "moan"(char *f, char *msg) + +import sys + +quis = '' +def ego(prog): + _ego(prog) + quis = _quis() + +def moan(msg): + _moan('%s', msg) +def die(msg, rc = 1): + _moan('%s', msg) + sys.exit(rc) + +#----- That's all, folks ---------------------------------------------------- diff --git a/select.pyx b/select.pyx new file mode 100644 index 0000000..28fe05d --- /dev/null +++ b/select.pyx @@ -0,0 +1,634 @@ +# -*-pyrex-*- +# +# $Id$ +# +# Selectery +# +# (c) 2005 Straylight/Edgeware +# + +#----- Licensing notice ----------------------------------------------------- +# +# This file is part of the Python interface to mLib. +# +# mLib/Python is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# mLib/Python is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with mLib/Python; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#----- External dependencies ------------------------------------------------ + +cdef extern from 'stddef.h': + ctypedef int size_t +cdef extern from 'string.h': + void memcpy(void *p, void *q, size_t n) +cdef extern from 'errno.h': + int errno + enum: + EINTR + EAGAIN +cdef extern from 'math.h': + double modf(double x, double *i) +cdef extern from 'string.h': + char *strerror(int err) +cdef extern from 'sys/time.h': + struct timeval: + int tv_sec + int tv_usec +cdef extern from 'sys/types.h': + pass +cdef extern from 'sys/socket.h': + struct sockaddr: + int sa_family + enum: + AF_INET + int getsockname(int fd, sockaddr *pa, size_t *psz) + int getpeername(int fd, sockaddr *pa, size_t *psz) +cdef extern from 'arpa/inet.h': + struct in_addr: + int s_addr + int inet_aton(char *rp, in_addr *ia) + char *inet_ntoa(in_addr ia) +cdef extern from 'netinet/in.h': + struct sockaddr_in: + int sin_family + in_addr sin_addr + int sin_port + int htons(int x) + int htonl(int x) + int ntohs(int x) + int ntohl(int x) +cdef extern from 'netdb.h': + struct hostent: + char *h_name + char **h_aliases + int h_addrtype + int h_length + char **h_addr_list + char *h_addr + int h_errno + +cdef extern from 'mLib/sel.h': + ctypedef struct sel_state: + pass + ctypedef struct sel_file: + int fd + ctypedef struct sel_timer: + pass + enum: + SEL_READ + SEL_WRITE + SEL_EXC + void sel_init(sel_state *s) + void sel_initfile(sel_state *s, sel_file *f, int fd, unsigned mode, + void (*func)(int fd, unsigned mode, void *arg), + void *arg) + void sel_force(sel_file *f) + void sel_addfile(sel_file *f) + void sel_rmfile(sel_file *f) + void sel_addtimer(sel_state *s, sel_timer *t, timeval *tv, + void (*func)(timeval *tv, void *arg), + void *arg) + void sel_rmtimer(sel_timer *t) + int sel_select(sel_state *s) except * + +cdef extern from 'mLib/conn.h': + ctypedef struct conn: + pass + int conn_fd(conn *c, sel_state *s, int fd, + void (*func)(int fd, void *arg), void *arg) + void conn_kill(conn *c) + +cdef extern from 'mLib/bres.h': + ctypedef struct bres_client: + pass + void bres_byname(bres_client *r, char *name, + void (*func)(hostent *h, void *arg), void *arg) + void bres_byaddr(bres_client *r, in_addr addr, + void (*func)(hostent *h, void *arg), void *arg) + void bres_abort(bres_client *r) + void bres_exec(char *null) + void bres_init(sel_state *s) + +cdef extern from 'mLib/sig.h': + ctypedef struct sig: + pass + void sig_add(sig *s, int n, void (*func)(int n, void *arg), void *arg) + void sig_remove(sig *s) + void sig_init(sel_state *s) + +cdef extern from 'mLib/lbuf.h': + cdef struct lbuf: + int f + int delim + size_t sz + enum: + LBUF_ENABLE + LBUF_CRLF + LBUF_STRICTCRLF + +cdef extern from 'mLib/selbuf.h': + ctypedef struct selbuf: + sel_file reader + lbuf b + void selbuf_enable(selbuf *b) + void selbuf_disable(selbuf *b) + void selbuf_setsize(selbuf *b, size_t sz) + void selbuf_init(selbuf *b, sel_state *s, int fd, + void (*func)(char *s, size_t len, void *arg), void *arg) + void selbuf_destroy(selbuf *b) + +cdef extern from 'mLib/pkbuf.h': + ctypedef struct pkbuf: + int f + int want + enum: + PKBUF_ENABLE + +cdef extern from 'mLib/selpk.h': + ctypedef struct selpk: + sel_file reader + pkbuf pk + void selpk_enable(selpk *b) + void selpk_disable(selpk *b) + void selpk_want(selpk *b, size_t sz) + void selpk_init(selpk *b, sel_state *s, int fd, + void (*func)(unsigned char *p, size_t n, + pkbuf *pk, size_t *keep, void *arg), + void *arg) + void selpk_destroy(selpk *b) + +cdef extern from 'mLib/ident.h': + ctypedef struct ident_request: + pass + enum: + IDENT_USERID + IDENT_ERROR + IDENT_BAD + struct ident_userid: + char *os + char *user + union ident_u: + ident_userid userid + char *error + ctypedef struct ident_reply: + int sport + int dport + int type + ident_u u + void ident(ident_request *rq, sel_state *s, + sockaddr_in *local, sockaddr_in *remote, + void (*func)(ident_reply *r, void *arg), + void *arg) + void ident_abort(ident_request *rq) + +cdef extern from 'Python.h': + object PyString_FromStringAndSize(char *p, int len) + int PyString_AsStringAndSize(obj, char **p, int *len) except -1 + int PyObject_AsReadBuffer(obj, void **buf, int *len) except -1 + int PyObject_TypeCheck(obj, ty) + +cdef extern from 'grim.h': + int PSIZEOF(void *x) + +import socket +import signal + +#----- Utility functions ---------------------------------------------------- + +cdef _oserror(): + raise OSError, (errno, strerror(errno)) + +cdef object _tobool(int i): + if i: + return True + else: + return False + +cdef int _getfd(object fdobj): + try: + return fdobj + except TypeError: + return fdobj.fileno() + +#----- The global select state ---------------------------------------------- + +cdef sel_state sel + +sel_init(&sel) +bres_init(&sel) +bres_exec(NULL) +sig_init(&sel) + +def select(): + if sel_select(&sel) and errno != EINTR and errno != EAGAIN: + _oserror() + +#----- File selectors ------------------------------------------------------- + +READ = SEL_READ +WRITE = SEL_WRITE +EXCEPT = SEL_EXC + +cdef class File: + cdef sel_file f + cdef int _activep + cdef readonly unsigned mode + def __new__(me, fd, int mode = SEL_READ): + if (mode != SEL_READ and + mode != SEL_WRITE and + mode != SEL_EXC): + raise ValueError, 'bad select mode' + sel_initfile(&sel, &me.f, _getfd(fd), mode, _filefunc, me) + me._activep = 0 + me.mode = mode + def __dealloc__(me): + if me._activep: + sel_rmfile(&me.f) + property fd: + def __get__(me): + return me.f.fd + property activep: + def __get__(me): + return _tobool(me._activep) + def enable(me): + if me._activep: + raise ValueError, 'already enabled' + sel_addfile(&me.f) + me._activep = 1 + return me + def disable(me): + if not me._activep: + raise ValueError, 'already disabled' + sel_rmfile(&me.f) + me._activep = 0 + return me + def force(me): + sel_force(&me.f) + return me + def ready(me): + pass + +cdef void _filefunc(int fd, unsigned mode, void *arg): + cdef File sf + sf = arg + sf.ready() + +#----- Timer selectors ------------------------------------------------------ + +cdef double _tvtofloat(timeval *tv): + return tv.tv_sec + (tv.tv_usec / 1000000) +cdef void _floattotv(timeval *tv, double t): + cdef double s, us + us = modf(t, &s) + tv.tv_sec = s + tv.tv_usec = us * 1000000 + +cdef class Timer: + cdef sel_timer t + cdef int _activep + cdef readonly double time + def __new__(me, double when): + cdef timeval tv + _floattotv(&tv, when) + sel_addtimer(&sel, &me.t, &tv, _timerfunc, me) + me._activep = 1 + me.time = when + def __dealloc__(me): + if me._activep: + sel_rmtimer(&me.t) + property activep: + def __get__(me): + return _tobool(me._activep) + def kill(me): + if not me._activep: + raise ValueError, 'already dead' + sel_rmtimer(&me.t) + me._activep = 0 + return me + def timer(me, now): + pass + +cdef void _timerfunc(timeval *now, void *arg): + cdef Timer st + st = arg + st._activep = 0 + st.timer(_tvtofloat(now)) + +#----- Connections ---------------------------------------------------------- + +cdef class Connect: + cdef conn c + cdef int _activep + cdef readonly object socket + def __new__(me, sk): + conn_fd(&me.c, &sel, sk.fileno(), _connfunc, me) + me._activep = 1 + me.socket = sk + def __dealloc__(me): + if me._activep: + conn_kill(&me.c) + property activep: + def __get__(me): + return _tobool(me._activep) + def kill(me): + if not me._activep: + raise ValueError, 'already dead' + conn_kill(&me.c); + me._activep = 0 + return me + def connected(me): + pass + def error(me, errno, strerror): + pass + +cdef void _connfunc(int fd, void *arg): + cdef Connect c + c = arg + c._activep = 0 + if fd == -1: + c.socket = None + c.error(errno, strerror(errno)) + else: + c.connected() + +#----- Background name resolution ------------------------------------------- + +cdef class Resolve: + cdef bres_client r + cdef int _activep + def __init__(me, *hunoz, **hukairz): + raise TypeError, 'abstract class' + property activep: + def __get__(me): + return _tobool(me._activep) + def kill(me): + if not me._activep: + raise ValueError, 'already dead' + bres_abort(&me.r) + return me + def resolved(me, h): + pass + def failed(me): + pass + +cdef class ResolveByName (Resolve): + def __new__(me, char *name): + bres_byname(&me.r, name, _resfunc, me) + me._activep = 1 + def __init__(me, name): + pass + +cdef class ResolveByAddr (Resolve): + def __new__(me, char *addr): + cdef in_addr ia + if not inet_aton(addr, &ia): + raise TypeError, 'bad IP address' + bres_byaddr(&me.r, ia, _resfunc, me) + me._activep = 1 + def __init__(me, addr): + pass + +cdef void _resfunc(hostent *h, void *arg): + cdef Resolve r + cdef int i + r = arg + r._activep = 0 + if h is NULL: + r.failed(r) + else: + alias = [] + addr = [] + i = 0 + while h.h_aliases[i]: + alias.append(h.h_aliases[i]) + i = i + 1 + i = 0 + while h.h_addr_list[i]: + addr.append(inet_ntoa((h.h_addr_list[i])[0])) + i = i + 1 + r.resolved(h.h_name, alias, addr) + +#----- Signal handling ------------------------------------------------------ + +cdef class Signal: + cdef sig s + cdef int _activep + cdef readonly int signal + def __new__(me, int sig): + if sig < 0 or sig >= signal.NSIG: + raise ValueError, 'signal number out of range' + me.signal = sig + me._activep = 0 + def __dealloc__(me): + if me._activep: + sig_remove(&me.s) + def enable(me): + if me._activep: + raise ValueError, 'already enabled' + sig_add(&me.s, me.signal, _sigfunc, me) + me._activep = 1 + return me + def disable(me): + if not me._activep: + raise ValueError, 'already disabled' + sig_remove(&me.s) + me._activep = 0 + return me + def signalled(me): + pass + +cdef void _sigfunc(int sig, void *arg): + cdef Signal s + s = arg + s.signalled() + +#----- Line buffers --------------------------------------------------------- + +CRLF = LBUF_CRLF +STRICTCRLF = LBUF_STRICTCRLF + +cdef class LineBuffer: + cdef selbuf b + def __new__(me, fd): + selbuf_init(&me.b, &sel, _getfd(fd), _lbfunc, me) + selbuf_disable(&me.b) + def __dealloc__(me): + selbuf_destroy(&me.b) + property activep: + def __get__(me): + return _tobool(me.b.b.f & LFBUF_ENABLE) + property delim: + def __get__(me): + if me.b.b.delim == LBUF_CRLF or me.b.b.delim == LBUF_STRICTCRLF: + return me.b.b.delim + else: + return chr(me.b.b.delim) + def __set__(me, d): + if d == LBUF_CRLF or d == LBUF_STRICTCRLF: + me.b.b.delim = d + else: + me.b.b.delim = ord(d) + property size: + def __get__(me): + return me.b.b.sz + def __set__(me, sz): + selbuf_setsize(&me.b, sz) + def enable(me): + if me.b.b.f & LBUF_ENABLE: + raise ValueError, 'already enabled' + selbuf_enable(&me.b) + return me + def disable(me): + if not (me.b.b.f & LBUF_ENABLE): + raise ValueError, 'already disabled' + selbuf_disable(&me.b) + return me + def line(me, line): + pass + def eof(me): + pass + +cdef void _lbfunc(char *s, size_t n, void *arg): + cdef LineBuffer sb + sb = arg + if s is NULL: + sb.eof() + else: + sb.line(PyString_FromStringAndSize(s, n)) + +#----- Packet buffers ------------------------------------------------------- + +cdef class PacketBuffer: + cdef selpk p + def __new__(me, fd): + selpk_init(&me.p, &sel, _getfd(fd), _pkfunc, me) + selpk_disable(&me.p) + def __dealloc__(me): + selpk_destroy(&me.p) + property activep: + def __get__(me): + return _to_bool(me.p.pk.f & PKBUF_ENABLE) + property want: + def __get__(me): + return me.p.pk.want + def __set__(me, n): + selpk_want(&me.p, n) + def enable(me): + if me.p.pk.f & PKBUF_ENABLE: + raise ValueError, 'already enabled' + selpk_enable(&me.p) + return me + def disable(me): + if not (me.p.pk.f & PKBUF_ENABLE): + raise ValueError, 'already disabled' + selpk_disable(&me.p) + return me + def packet(me, pk): + return None + def eof(me): + pass + +cdef void _pkfunc(unsigned char *p, size_t n, pkbuf *pk, + size_t *keep, void *arg): + cdef PacketBuffer pb + cdef void *rp + cdef int rn + pb = arg + if p is NULL: + pb.eof() + else: + r = pb.packet(PyString_FromStringAndSize(p, n)) + if r is not None: + PyObject_AsReadBuffer(r, &rp, &rn) + if rn > n: + raise ValueError, 'remaining buffer too large' + if rn: + memcpy(p + n - rn, rp, rn) + keep[0] = rn + +#----- Ident client --------------------------------------------------------- + +cdef _inaddr_frompy(sockaddr_in *sin, addr): + cdef int port + if len(addr) != 2: + raise TypeError, 'want address/port pair' + a = addr[0] + if not inet_aton(a, &sin.sin_addr): + raise TypeError, 'bad IP address' + port = addr[1] + if not (0 <= port < 65536): + raise TypeError, 'port number out of range' + sin.sin_port = htons(port) + +cdef _inaddr_topy(sockaddr_in *sin): + return inet_ntoa(sin.sin_addr), ntohs(sin.sin_port) + +cdef class Identify: + cdef ident_request irq + cdef int _activep + cdef readonly localaddr + cdef readonly remoteaddr + def __new__(me, sk): + cdef sockaddr_in s_in, s_out + cdef size_t sz_in, sz_out + cdef int fd + if PyObject_TypeCheck(sk, socket.SocketType): + fd = sk.fileno() + sz_in = PSIZEOF(&s_in) + sz_out = PSIZEOF(&s_out) + if getsockname(fd, &s_in, &sz_in) or \ + getpeername(fd, &s_out, &sz_out): + _oserror() + if s_in.sin_family != AF_INET or s_out.sin_family != AF_INET: + raise TypeError, 'must be internet socket' + elif len(sk) != 2: + raise TypeError, 'want pair of addresses' + else: + _inaddr_frompy(&s_in, sk[0]) + _inaddr_frompy(&s_out, sk[1]) + ident(&me.irq, &sel, &s_in, &s_out, _identfunc, me) + me.localaddr = _inaddr_topy(&s_in) + me.remoteaddr = _inaddr_topy(&s_out) + me._activep = 1 + def __dealloc__(me): + if me._activep: + ident_abort(&me.irq) + property activep: + def __get__(me): + return _tobool(me._activep) + def kill(me): + if not me._activep: + raise ValueError, 'already disabled' + ident_abort(&me.irq) + me._activep = 0 + def user(me, os, user): + pass + def bad(me): + pass + def error(me, error): + pass + def failed(me, errno, strerror): + pass + +cdef void _identfunc(ident_reply *i, void *arg): + cdef Identify id + id = arg + id._activep = 0 + if i.type == IDENT_BAD: + ii.bad() + elif i.type == IDENT_ERROR: + ii.error(i.u.error) + elif i.type == IDENT_USER: + ii.user(i.u.userid.os, i.u.userid.user) + +#----- That's all, folks ---------------------------------------------------- diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..f8c091b --- /dev/null +++ b/setup.py @@ -0,0 +1,102 @@ +from distutils.core import setup, Extension +from Pyrex.Distutils import build_ext +from os import * +from errno import * +import sre +import sys +from sys import stdin, stdout, stderr + +incdirs = [] +libdirs = [] +libs = [] + +def progoutput(cmd): + p = popen(cmd) + out = p.readline() + if p.read() != '': raise 'extra junk from %s' % cmd + p.close() + return out.rstrip('\n') + +def libconfig(lib, ver): + config = lib + '-config' + if system('%s --check %s' % (config, ver)): + raise '%s version %s not found' % (lib, ver) + version = progoutput('%s --version' % config) + for i in progoutput('%s --cflags' % config).split(): + if i[:2] == '-I': incdirs.append(i[2:]) + else: raise 'strange cflags item %s' % i + for i in progoutput('%s --libs' % config).split(): + if i[:2] == '-L': libdirs.append(i[2:]) + elif i[:2] == '-l': libs.append(i[2:]) + else: raise 'strange libs item %s' % i + +def uniquify(l): + u = {} + o = [] + for i in l: + if i not in u: + o.append(i) + u[i] = 1 + return o + +libconfig('catacomb', '2.1.0') +libconfig('mLib', '2.0.3') + +def needs_update_p(target, sources): + if not path.exists(target): return True + t_target = stat(target).st_mtime + for s in sources: + if stat(s).st_mtime > t_target: return True + return False + +rx_subst = sre.compile(r'\%(\w+)\%') + +def getsource(src): + br = src.find('[') + if br >= 0: + if src[-1] != ']': + raise SyntaxError, 'bad auto file' + subst = src[br + 1:-1] + src = src[:br] + x = subst.split(':') + infile = x[0] + if needs_update_p(src, [infile]): + print 'creating %s from %s...' % (src, infile) + d = dict([i.split('/', 1) for i in x[1:]]) + out = file(src + '.new', 'w') + for line in file(infile): + out.write(rx_subst.sub((lambda m: d[m.group(1)]), line)) + out.close() + rename(src + '.new', src) + return src + +def mlibext(src): + col = src.find('!') + if col >= 0: + mod = src[:col] + srcs = [getsource(s) for s in src[col + 1:].split(',')] + else: + src = getsource(src) + mod, hunoz = src.split('.', 1) + srcs = [src] + return Extension('mLib.' + mod, srcs, + ##extra_compile_args = ['-O0'], + include_dirs = uniquify(incdirs), + library_dirs = uniquify(libdirs), + libraries = uniquify(libs)) + +setup(name = 'mLib-python', + version = '1.0.0', + description = 'Python interface to mLib utilities library', + author = 'Straylight/Edgeware', + author_email = 'mdw@distorted.org.uk', + license = 'GNU General Public License', + packages = ['mLib'], + ext_modules = [mlibext(x) for x in ''' + select.pyx crc32.pyx unihash.pyx report.pyx + base64.pyx[codec.pyx.in:PREFIX/base64] + base32.pyx[codec.pyx.in:PREFIX/base32] + hex.pyx[codec.pyx.in:PREFIX/hex] + array.c sym.pyx atom!atom-base.c,atom.pyx + '''.split()], + cmdclass = {'build_ext': build_ext}) diff --git a/sym.pyx b/sym.pyx new file mode 100644 index 0000000..4df3d9e --- /dev/null +++ b/sym.pyx @@ -0,0 +1,267 @@ +# -*-pyrex-*- +# +# $Id$ +# +# Symbol table, using universal hashing +# +# (c) 2005 Straylight/Edgeware +# + +#----- Licensing notice ----------------------------------------------------- +# +# This file is part of the Python interface to mLib. +# +# mLib/Python is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# mLib/Python is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with mLib/Python; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +cdef extern from 'mLib/sym.h': + ctypedef struct sym_table: + pass + ctypedef struct sym_base: + pass + ctypedef struct sym_iter: + pass + void sym_create(sym_table *t) + void sym_destroy(sym_table *t) + void *sym_find(sym_table *t, char *n, long l, int sz, unsigned *f) + void sym_remove(sym_table *t, void *b) + char *SYM_NAME(void *b) + int SYM_LEN(void *b) + void sym_mkiter(sym_iter *i, sym_table *t) + void *sym_next(sym_iter *i) + +cdef extern from 'grim.h': + int PSIZEOF(void *p) + +cdef extern from 'Python.h': + int PyObject_AsReadBuffer(obj, void **buf, int *len) except -1 + PyString_FromStringAndSize(char *p, int n) + ctypedef struct PyObject: + pass + void Py_INCREF(PyObject *obj) + void Py_DECREF(PyObject *obj) + +cdef struct entry: + sym_base _b + PyObject *v + +cdef entry *_find(sym_table *t, object k, unsigned *f) except ?NULL: + cdef void *p + cdef int n + cdef entry *e + PyObject_AsReadBuffer(key, &p, &n) + if f: + f[0] = 0 + e = sym_find(t, p, n, PSIZEOF(e), f) + if not f[0]: + e.v = NULL + else: + e = sym_find(t, p, n, 0, NULL) + return e + +cdef _eget(entry *e): + Py_INCREF(e.v) + return e.v + +cdef void _eset(entry *e, v): + if e.v: + Py_DECREF(e.v) + e.v = v + Py_INCREF(e.v) + +cdef void _edel(sym_table *t, entry *e): + if e.v: + Py_DECREF(e.v) + sym_remove(t, e) + +cdef _key(entry *e): + return PyString_FromStringAndSize(SYM_NAME(e), SYM_LEN(e)) + +cdef class Table: + cdef sym_table _t + def __new__(me, *hunoz, **hukairz): + sym_create(&me._t) + def __init__(me, stuff = None, **kw): + me.update(stuff, kw) + def __getitem__(me, key): + cdef entry *e + e = _find(&me._t, key, NULL) + if not e: + raise KeyError, key + return _eget(e) + def __setitem__(me, key, value): + cdef unsigned f + _eset(_find(&me._t, key, &f), value) + def __delitem__(me, key): + cdef entry *e + cdef unsigned f + e = _find(&me._t, key, &f) + if not e: + raise KeyError, key + _edel(&me._t, e) + def get(me, key, default = None): + cdef entry *e + e = _find(&me._t, key, NULL) + if not e: + return default + return _eget(e) + def setdefault(me, key, default = None): + cdef entry *e + cdef unsigned f + e = _find(&me._t, key, &f) + if f: + return _eget(e) + else: + _eset(e, default) + return default + def pop(me, key, default = None): + cdef entry *e + e = _find(&me._t, key, NULL) + if not e: + return default + rc = _eget(e) + _edel(&me._t, e) + return rc + def popitem(me): + cdef entry *e + cdef sym_iter i + sym_mkiter(&i, &me._t) + e = sym_next(&i) + if not e: + raise ValueError, 'popitem(): table is empty' + return _key(e), _eget(e) + def keys(me): + cdef sym_iter i + cdef entry *e + l = [] + sym_mkiter(&i, &me._t) + while 1: + e = sym_next(&i) + if not e: + break + l.append(_key(e)) + return l + def values(me): + cdef sym_iter i + cdef entry *e + l = [] + sym_mkiter(&i, &me._t) + while 1: + e = sym_next(&i) + if not e: + break + l.append(_eget(e)) + return l + def items(me): + cdef sym_iter i + cdef entry *e + l = [] + sym_mkiter(&i, &me._t) + while 1: + e = sym_next(&i) + if not e: + break + l.append((_key(e), _eget(e))) + return l + def clear(me): + cdef sym_iter i + cdef entry *e + sym_mkiter(&i, &me._t) + while 1: + e = sym_next(&i) + if not e: + break + _edel(&me._t, e) + return me + def __dealloc__(me): + cdef sym_iter i + cdef entry *e + sym_mkiter(&i, &me._t) + while 1: + e = sym_next(&i) + if not e: + break + _edel(&me._t, e) + sym_destroy(&me._t) + def iterkeys(me): + return KeyIter(me) + def __iter__(me): + return KeyIter(me) + def itervalues(me): + return ValueIter(me) + def iteritems(me): + return ItemIter(me) + def update(me, stuff = None, **kw): + cdef unsigned f + if stuff is None: + pass + elif hasattr(stuff, 'itemiter'): + for k, v in stuff.itemiter: + _eset(_find(&me._t, k, &f), v) + elif hasattr(stuff, 'keys'): + for k in stuff.keys(): + _eset(_find(&me._t, k, &f), stuff[k]) + else: + for k, v in stuff: + _eset(_find(&me._t, k, &f), v) + for k, v in kw.iteritems(): + _eset(_find(&me._t, k, &f), v) + return me + +cdef class KeyIter: + cdef Table _t + cdef sym_iter _i + def __new__(me, Table t): + me._t = t + sym_mkiter(&me._i, &t._t) + def __iter__(me): + return me + def __next__(me): + cdef entry *e + e = sym_next(&me._i) + if not e: + raise StopIteration + return _key(e) + +cdef class ValueIter: + cdef Table _t + cdef sym_iter _i + def __new__(me, Table t): + me._t = t + sym_mkiter(&me._i, &t._t) + def __iter__(me): + return me + def __next__(me): + cdef entry *e + e = sym_next(&me._i) + if not e: + raise StopIteration + return _eget(e) + +cdef class ItemIter: + cdef Table _t + cdef sym_iter _i + def __new__(me, Table t): + me._t = t + sym_mkiter(&me._i, &t._t) + def __iter__(me): + return me + def __next__(me): + cdef entry *e + e = sym_next(&me._i) + if not e: + raise StopIteration + return _key(e), _eget(e) + +#----- That's all, folks ---------------------------------------------------- diff --git a/unihash.pyx b/unihash.pyx new file mode 100644 index 0000000..f52e4af --- /dev/null +++ b/unihash.pyx @@ -0,0 +1,86 @@ +# -*-pyrex-*- +# +# $Id$ +# +# Universal hashing interface +# +# (c) 2005 Straylight/Edgeware +# + +#----- Licensing notice ----------------------------------------------------- +# +# This file is part of the Python interface to mLib. +# +# mLib/Python is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# mLib/Python is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with mLib/Python; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +cdef extern from 'mLib/unihash.h': + ctypedef int uint32 + ctypedef struct unihash_info: + pass + void unihash_setkey(unihash_info *i, uint32 k) + uint32 UNIHASH_INIT(unihash_info *i) + uint32 unihash_hash(unihash_info *i, uint32 a, void *p, int sz) + unihash_info unihash_global + +cdef extern from 'limits.h': + enum: + LONG_MAX + +cdef extern from 'Python.h': + int PyObject_AsReadBuffer(obj, void **buf, int *len) except -1 + PyInt_FromLong(long i) + PyLong_FromUnsignedLong(unsigned long i) + +cdef _u32(uint32 x): + if x < LONG_MAX: + return PyInt_FromLong(x) + else: + return PyLong_FromUnsignedLong(x) + +def setglobalkey(uint32 k): + unihash_setkey(&unihash_global, k) + +cdef class Key: + cdef unihash_info _i + cdef uint32 _k + def __new__(me, uint32 k): + unihash_setkey(&me._i, k) + me._k = k + property k: + def __get__(me): + return _u32(me._k) + +cdef class Unihash: + cdef uint32 _a + cdef readonly Key key + cdef unihash_info *_i + def __new__(me, key = None): + cdef Key k + me.key = key + if key is None: + me._i = &unihash_global + else: + k = key + me._i = &k._i + me._a = UNIHASH_INIT(me._i) + def chunk(me, data): + cdef void *p + cdef int n + PyObject_AsReadBuffer(data, &p, &n) + me._a = unihash_hash(me._i, me._a, p, n) + def done(me): + return _u32(me._a) + +#----- That's all, folks ---------------------------------------------------- -- [mdw]