chiark / gitweb /
Initial import.
authormdw <mdw>
Thu, 13 Oct 2005 17:07:47 +0000 (17:07 +0000)
committermdw <mdw>
Thu, 13 Oct 2005 17:07:47 +0000 (17:07 +0000)
17 files changed:
.skelrc [new file with mode: 0644]
MANIFEST.in [new file with mode: 0644]
Makefile [new file with mode: 0644]
README [new file with mode: 0644]
array.c [new file with mode: 0644]
atom-base.c [new file with mode: 0644]
atom.h [new file with mode: 0644]
atom.pyx [new file with mode: 0644]
codec.pyx.in [new file with mode: 0644]
crc32.pyx [new file with mode: 0644]
grim.h [new file with mode: 0644]
mLib/__init__.py [new file with mode: 0644]
report.pyx [new file with mode: 0644]
select.pyx [new file with mode: 0644]
setup.py [new file with mode: 0644]
sym.pyx [new file with mode: 0644]
unihash.pyx [new file with mode: 0644]

diff --git a/.skelrc b/.skelrc
new file mode 100644 (file)
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 (file)
index 0000000..b051d6b
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
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 <Python.h>
+
+#include <string.h>
+
+#include <mLib/darray.h>
+#include <mLib/dstr.h>
+#include <mLib/exc.h>
+
+#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 (file)
index 0000000..44b915b
--- /dev/null
@@ -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 <Python.h>
+
+#include <mLib/atom.h>
+#include <mLib/assoc.h>
+#include <mLib/dstr.h>
+
+#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 (file)
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 <Python.h>
+
+#include <mLib/atom.h>
+#include <mLib/assoc.h>
+
+/*----- 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 (file)
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 = <entry *>assoc_find(t, a, PSIZEOF(e), f)
+    if not f[0]:
+      e.v = NULL
+  else:
+    e = <entry *>assoc_find(t, a, 0, NULL)
+  return e
+
+cdef _key(entry *e):
+  return atom_pywrap(ASSOC_ATOM(<void *>e))
+
+cdef _eget(entry *e):
+  Py_INCREF(e.v)
+  return <object>e.v
+
+cdef void _eset(entry *e, v):
+  if e.v:
+    Py_DECREF(e.v)
+  e.v = <PyObject *>v
+  Py_INCREF(e.v)
+
+cdef void _edel(assoc_table *t, entry *e):
+  if e.v:
+    Py_DECREF(e.v)
+  assoc_remove(t, <void *>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 = <entry *>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 = <entry *>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 = <entry *>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 = <entry *>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 = <entry *>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 = <entry *>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 = <entry *>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 = <entry *>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 = <entry *>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 (file)
index 0000000..60d9714
--- /dev/null
@@ -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 (file)
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 (file)
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 <Python.h>
+
+/*----- 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 (file)
index 0000000..2ae2839
--- /dev/null
@@ -0,0 +1 @@
+pass
diff --git a/report.pyx b/report.pyx
new file mode 100644 (file)
index 0000000..e3749b4
--- /dev/null
@@ -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 = '<UNNAMED>'
+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 (file)
index 0000000..28fe05d
--- /dev/null
@@ -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, <void *>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 = <File>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, <void *>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 = <Timer>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, <void *>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 = <Connect>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, <void *>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, <void *>me)
+    me._activep = 1
+  def __init__(me, addr):
+    pass
+
+cdef void _resfunc(hostent *h, void *arg):
+  cdef Resolve r
+  cdef int i
+  r = <Resolve>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((<in_addr *>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, <void *>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 = <Signal>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, <void *>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 = <LineBuffer>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, <void *>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 = <PacketBuffer>arg
+  if p is NULL:
+    pb.eof()
+  else:
+    r = pb.packet(PyString_FromStringAndSize(<char *>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, <sockaddr *>&s_in, &sz_in) or \
+         getpeername(fd, <sockaddr *>&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, <void *>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 = <Identify>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 (file)
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 (file)
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 = <entry *>sym_find(t, <char *>p, n, PSIZEOF(e), f)
+    if not f[0]:
+      e.v = NULL
+  else:
+    e = <entry *>sym_find(t, <char *>p, n, 0, NULL)
+  return e
+
+cdef _eget(entry *e):
+  Py_INCREF(e.v)
+  return <object>e.v
+
+cdef void _eset(entry *e, v):
+  if e.v:
+    Py_DECREF(e.v)
+  e.v = <PyObject *>v
+  Py_INCREF(e.v)
+
+cdef void _edel(sym_table *t, entry *e):
+  if e.v:
+    Py_DECREF(e.v)
+  sym_remove(t, <void *>e)
+
+cdef _key(entry *e):
+  return PyString_FromStringAndSize(SYM_NAME(<void *>e), SYM_LEN(<void *>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 = <entry *>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 = <entry *>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 = <entry *>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 = <entry *>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 = <entry *>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 = <entry *>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 = <entry *>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 = <entry *>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 = <entry *>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 (file)
index 0000000..f52e4af
--- /dev/null
@@ -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 ----------------------------------------------------