pyke/pyke-mLib.c: Raise `OverflowError' on out-of-range inputs.
[catacomb-python] / buffer.c
index 59a85bf..d0df068 100644 (file)
--- a/buffer.c
+++ b/buffer.c
 
 #include "catacomb-python.h"
 
-/*----- Data structures ---------------------------------------------------*/
-
-typedef struct buf_pyobj {
-  PyObject_HEAD
-  buf b;
-  PyObject *sub;
-} buf_pyobj;
-
-static PyTypeObject *rbuf_pytype, *wbuf_pytype;
-#define RBUF_PYCHECK(o) PyObject_TypeCheck((o), rbuf_pytype)
-#define WBUF_PYCHECK(o) PyObject_TypeCheck((o), wbuf_pytype)
-#define BUF_B(o) (&((buf_pyobj *)(o))->b)
-#define BUF_SUB(o) (((buf_pyobj *)(o))->sub)
-
-/*----- Exceptions --------------------------------------------------------*/
-
-static PyObject *buferr;
-
-#define BUFERR() do { PyErr_SetNone(buferr); goto end; } while (0)
-
 /*----- Read buffers ------------------------------------------------------*/
 
+PyTypeObject *rbuf_pytype;
+
 static PyObject *rbuf_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
 {
-  char *p, *q;
-  int n;
+  struct bin in;
+  void *q;
   buf_pyobj *me = 0;
-  static char *kwlist[] = { "data", 0 };
+  static const char *const kwlist[] = { "data", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#:new", kwlist, &p, &n))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", KWLIST, convbin, &in))
     goto end;
-  q = xmalloc(n);
-  memcpy(q, p, n);
+  q = xmalloc(in.sz);
+  memcpy(q, in.p, in.sz);
   me = (buf_pyobj *)ty->tp_alloc(ty, 0);
-  me->sub = 0;
-  buf_init(&me->b, q, n);
+  me->sub = 0; me->lk = 0;
+  buf_init(&me->b, q, in.sz);
 end:
   return ((PyObject *)me);
 }
 
 static void buf_pydealloc(PyObject *me)
 {
-  if (BUF_SUB(me))
-    Py_DECREF(BUF_SUB(me));
-  else
-    xfree(BBASE(BUF_B(me)));
+  assert(!BUF_LK(me));
+  if (BUF_SUB(me)) Py_DECREF(BUF_SUB(me));
+  else xfree(BBASE(BUF_B(me)));
   FREEOBJ(me);
 }
 
-static int rbuf_pysegcount(PyObject *me, int *nn)
-  { if (nn) *nn = BSZ(BUF_B(me)); return (1); }
-
-static int rbuf_pyreadbuf(PyObject *me, int seg, void **q)
-  { assert(seg == 0); *q = BCUR(BUF_B(me)); return (BLEFT(BUF_B(me))); }
+#ifdef PY3
+  static int rbuf_pygetbuf(PyObject *me, Py_buffer *vw, int f)
+  {
+    buf *b = BUF_B(me);
+    return (PyBuffer_FillInfo(vw, me, BCUR(b), BLEFT(b), 1, f));
+  }
+#else
+  static Py_ssize_t rbuf_pysegcount(PyObject *me, Py_ssize_t *nn)
+    { if (nn) *nn = BSZ(BUF_B(me)); return (1); }
+  static Py_ssize_t rbuf_pyreadbuf(PyObject *me, Py_ssize_t seg, void **q)
+    { assert(seg == 0); *q = BCUR(BUF_B(me)); return (BLEFT(BUF_B(me))); }
+#endif
 
 static PyObject *rbmeth_skip(PyObject *me, PyObject *arg)
 {
   size_t n;
 
   if (!PyArg_ParseTuple(arg, "O&:skip", convszt, &n)) goto end;
-  if (!buf_get(BUF_B(me), n)) BUFERR();
+  if (!buf_get(BUF_B(me), n)) BUFERR("buffer exhausted");
   RETURN_ME;
 end:
   return (0);
@@ -100,31 +88,31 @@ static PyObject *rbmeth_get(PyObject *me, PyObject *arg)
   size_t n;
 
   if (!PyArg_ParseTuple(arg, "O&:get", convszt, &n)) goto end;
-  if ((p = buf_get(BUF_B(me), n)) == 0) BUFERR();
+  if ((p = buf_get(BUF_B(me), n)) == 0) BUFERR("buffer exhausted");
   return (bytestring_pywrap(p, n));
 end:
   return (0);
 }
 
 #define RBMETH_GETU_(n, W, w)                                          \
-  static PyObject *rbmeth_getu##w(PyObject *me, PyObject *arg)         \
+  static PyObject *rbmeth_getu##w(PyObject *me)                                \
   {                                                                    \
     uint##n x;                                                         \
-    if (!PyArg_ParseTuple(arg, ":getu" #w)) goto end;                  \
-    if (buf_getu##w(BUF_B(me), &x)) BUFERR();                          \
-    return (getulong(x));                                              \
+    if (buf_getu##w(BUF_B(me), &x)) BUFERR("buffer exhausted");                \
+    if (MASK##W <= ULONG_MAX) return (getulong(x));                    \
+    else { kludge64 y; ASSIGN64(y, x); return (getk64(y)); }           \
   end:                                                                 \
     return (0);                                                                \
   }
 DOUINTCONV(RBMETH_GETU_)
 
 #define RBMETH_GETBLK_(n, W, w)                                                \
-  static PyObject *rbmeth_getblk##w(PyObject *me, PyObject *arg)       \
+  static PyObject *rbmeth_getblk##w(PyObject *me)                      \
   {                                                                    \
     size_t sz;                                                         \
     char *q;                                                           \
-    if (!PyArg_ParseTuple(arg, ":getblk" #w)) goto end;                        \
-    if ((q = buf_getmem##w(BUF_B(me), &sz)) == 0) BUFERR();            \
+    if ((q = buf_getmem##w(BUF_B(me), &sz)) == 0)                      \
+      BUFERR("buffer exhausted");                                      \
     return (bytestring_pywrap(q, sz));                                 \
   end:                                                                 \
     return (0);                                                                \
@@ -132,15 +120,15 @@ DOUINTCONV(RBMETH_GETU_)
 BUF_DOSUFFIXES(RBMETH_GETBLK_)
 
 #define RBMETH_GETBUF_(n, W, w)                                                \
-  static PyObject *rbmeth_getbuf##w(PyObject *me, PyObject *arg)       \
+  static PyObject *rbmeth_getbuf##w(PyObject *me)                      \
   {                                                                    \
     buf_pyobj *b;                                                      \
     buf bb;                                                            \
-    if (!PyArg_ParseTuple(arg, ":getbuf" #w)) goto end;                        \
-    if (buf_getbuf##w(BUF_B(me), &bb)) BUFERR();                       \
+    if (buf_getbuf##w(BUF_B(me), &bb)) BUFERR("buffer exhausted");     \
     b = PyObject_NEW(buf_pyobj, rbuf_pytype);                          \
     b->b = bb;                                                         \
     b->sub = me;                                                       \
+    b->lk = 0;                                                         \
     Py_INCREF(me);                                                     \
     return ((PyObject *)b);                                            \
   end:                                                                 \
@@ -148,21 +136,19 @@ BUF_DOSUFFIXES(RBMETH_GETBLK_)
   }
 BUF_DOSUFFIXES(RBMETH_GETBUF_)
 
-static PyObject *rbmeth_getmp(PyObject *me, PyObject *arg)
+static PyObject *rbmeth_getmp(PyObject *me)
 {
   mp *x;
-  if (!PyArg_ParseTuple(arg, ":getmp")) goto end;
-  if ((x = buf_getmp(BUF_B(me))) == 0) BUFERR();
+  if ((x = buf_getmp(BUF_B(me))) == 0) BUFERR("buffer exhausted");
   return (mp_pywrap(x));
 end:
   return (0);
 }
 
-static PyObject *rbmeth_getgf(PyObject *me, PyObject *arg)
+static PyObject *rbmeth_getgf(PyObject *me)
 {
   mp *x;
-  if (!PyArg_ParseTuple(arg, ":getgf")) goto end;
-  if ((x = buf_getmp(BUF_B(me))) == 0) BUFERR();
+  if ((x = buf_getmp(BUF_B(me))) == 0) BUFERR("buffer exhausted");
   return (gf_pywrap(x));
 end:
   return (0);
@@ -171,15 +157,15 @@ end:
 static PyObject *rbmeth_getecpt(PyObject *me, PyObject *arg, PyObject *kw)
 {
   PyObject *cobj = Py_None;
-  static char *kwlist[] = { "curve", 0 };
+  static const char *const kwlist[] = { "curve", 0 };
   ec pt = EC_INIT;
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O:getecpt", kwlist, &cobj))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O:getecpt", KWLIST, &cobj))
     goto end;
   if (cobj == Py_None) cobj = (PyObject *)ecpt_pytype;
   if (!PyType_Check(cobj) ||
       !PyType_IsSubtype((PyTypeObject *)cobj, ecpt_pytype))
     TYERR("expected elliptic curve type");
-  if (buf_getec(BUF_B(me), &pt)) BUFERR();
+  if (buf_getec(BUF_B(me), &pt)) BUFERR("buffer exhausted");
   return (ecpt_pywrapout(cobj, &pt));
 end:
   return (0);
@@ -187,14 +173,31 @@ end:
 
 static PyObject *rbmeth_getecptraw(PyObject *me, PyObject *arg)
 {
-  PyTypeObject *cobj = ecpt_pytype;
+  PyObject *cobj;
   ec pt = EC_INIT;
+  PyObject *rc = 0;
   if (!PyArg_ParseTuple(arg, "O!:getecptraw", eccurve_pytype, &cobj))
     goto end;
-  if (ec_getraw(ECCURVE_C(cobj), BUF_B(me), &pt)) BUFERR();
-  return (ecpt_pywrapout(cobj, &pt));
+  if (ec_getraw(ECCURVE_C(cobj), BUF_B(me), &pt)) BUFERR("buffer exhausted");
+  rc = ecpt_pywrapout(cobj, &pt);
 end:
-  return (0);
+  return (rc);
+}
+
+static PyObject *rbmeth_os2ecp(PyObject *me, PyObject *arg, PyObject *kw)
+{
+  PyObject *cobj;
+  ec pt = EC_INIT;
+  unsigned f = EC_XONLY | EC_LSB | EC_SORT | EC_EXPLY;
+  PyObject *rc = 0;
+  static const char *const kwlist[] = { "curve", "flags", 0 };
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!|O&:os2ecp", KWLIST,
+                                  eccurve_pytype, &cobj, convuint, &f))
+    goto end;
+  if (ec_os2ecp(ECCURVE_C(cobj), f, BUF_B(me), &pt)) VALERR("bad point");
+  rc = ecpt_pywrapout(cobj, &pt);
+end:
+  return (rc);
 }
 
 static PyObject *rbmeth_getge(PyObject *me, PyObject *arg)
@@ -203,7 +206,7 @@ static PyObject *rbmeth_getge(PyObject *me, PyObject *arg)
   ge *x = 0;
   if (!PyArg_ParseTuple(arg, "O!:getge", group_pytype, &gobj)) goto end;
   x = G_CREATE(GROUP_G(gobj));
-  if (G_FROMBUF(GROUP_G(gobj), BUF_B(me), x)) BUFERR();
+  if (G_FROMBUF(GROUP_G(gobj), BUF_B(me), x)) BUFERR("buffer exhausted");
   return (ge_pywrap(gobj, x));
 end:
   if (x) G_DESTROY(GROUP_G(gobj), x);
@@ -216,7 +219,7 @@ static PyObject *rbmeth_getgeraw(PyObject *me, PyObject *arg)
   ge *x = 0;
   if (!PyArg_ParseTuple(arg, "O!:getgeraw", group_pytype, &gobj)) goto end;
   x = G_CREATE(GROUP_G(gobj));
-  if (G_FROMRAW(GROUP_G(gobj), BUF_B(me), x)) BUFERR();
+  if (G_FROMRAW(GROUP_G(gobj), BUF_B(me), x)) BUFERR("buffer exhausted");
   return (ge_pywrap(gobj, x));
 end:
   if (x) G_DESTROY(GROUP_G(gobj), x);
@@ -243,49 +246,55 @@ end:
   return (-1);
 }
 
-static PyGetSetDef rbuf_pygetset[] = {
+static const PyGetSetDef rbuf_pygetset[] = {
 #define GETSETNAME(op, name) rb##op##_##name
-  GET  (size,                  "RBUF.size -> SIZE")
-  GET  (left,                  "RBUF.left -> REMAINDER")
-  GET  (endp,                  "RBUF.endp -> BOOL")
-  GETSET(offset,               "RBUF.offset -> OFFSET")
+  GET  (size,          "RBUF.size -> SIZE")
+  GET  (left,          "RBUF.left -> REMAINDER")
+  GET  (endp,          "RBUF.endp -> BOOL")
+  GETSET(offset,       "RBUF.offset -> OFFSET")
 #undef GETSETNAME
   { 0 }
 };
 
-static PyMethodDef rbuf_pymethods[] = {
+static const PyMethodDef rbuf_pymethods[] = {
 #define METHNAME(func) rbmeth_##func
-  METH (skip,                  "RBUF.skip(N)")
-  METH (get,                   "RBUF.get(N) -> BYTES")
+  METH (skip,          "RBUF.skip(N)")
+  METH (get,           "RBUF.get(N) -> BYTES")
 #define RBMETH_DECL_GETU_(n, W, w)                                     \
-    METH(getu##w, "RBUF.getu" #w "() -> INT")
+    NAMETH(getu##w,    "RBUF.getu" #w "() -> INT")
   DOUINTCONV(RBMETH_DECL_GETU_)
 #define RBMETH_DECL_GETBLK_(n, W, w)                                   \
-    METH(getblk##w, "RBUF.getblk" #w "() -> INT")
+    NAMETH(getblk##w,  "RBUF.getblk" #w "() -> BYTES")
   BUF_DOSUFFIXES(RBMETH_DECL_GETBLK_)
 #define RBMETH_DECL_GETBUF_(n, W, w)                                   \
-    METH(getbuf##w, "RBUF.getbuf" #w "() -> INT")
+    NAMETH(getbuf##w,  "RBUF.getbuf" #w "() -> RBUF'")
   BUF_DOSUFFIXES(RBMETH_DECL_GETBUF_)
-  METH (getmp,                 "RBUF.getmp() -> X")
-  METH (getgf,                 "RBUF.getgf() -> X")
-  KWMETH(getecpt,              "RBUF.getecpt(curve = None) -> P")
-  METH (getecptraw,            "RBUF.getecptraw(CURVE) -> P")
-  METH (getge,                 "RBUF.getge(GROUP) -> X")
-  METH (getgeraw,              "RBUF.getgeraw(GROUP) -> X")
+  NAMETH(getmp,                "RBUF.getmp() -> X")
+  NAMETH(getgf,                "RBUF.getgf() -> X")
+  KWMETH(getecpt,      "RBUF.getecpt([curve = None]) -> P")
+  METH (getecptraw,    "RBUF.getecptraw(CURVE) -> P")
+  KWMETH(os2ecp,       "RBUF.os2ecp(CURVE, [flags = ...]) -> P")
+  METH (getge,         "RBUF.getge(GROUP) -> X")
+  METH (getgeraw,      "RBUF.getgeraw(GROUP) -> X")
 #undef METHNAME
   { 0 }
 };
 
-static PyBufferProcs rbuf_pybuffer = {
+static const PyBufferProcs rbuf_pybuffer = {
+#ifdef PY3
+  rbuf_pygetbuf,                       /* @bf_getbuffer@ */
+  0,                                   /* @bf_releasebuffer@ */
+#else
   rbuf_pyreadbuf,                      /* @bf_getreadbuffer@ */
   0,                                   /* @bf_getwritebuffer@ */
   rbuf_pysegcount,                     /* @bf_getsegcount@ */
   0                                    /* @bf_getcharbuffer@ */
+#endif
 };
 
-static PyTypeObject rbuf_pytype_skel = {
-  PyObject_HEAD_INIT(0) 0,             /* Header */
-  "catacomb.ReadBuffer",               /* @tp_name@ */
+static const PyTypeObject rbuf_pytype_skel = {
+  PyVarObject_HEAD_INIT(0, 0)          /* Header */
+  "ReadBuffer",                                /* @tp_name@ */
   sizeof(buf_pyobj),                   /* @tp_basicsize@ */
   0,                                   /* @tp_itemsize@ */
 
@@ -303,12 +312,12 @@ static PyTypeObject rbuf_pytype_skel = {
   0,                                   /* @tp_str@ */
   0,                                   /* @tp_getattro@ */
   0,                                   /* @tp_setattro@ */
-  &rbuf_pybuffer,                      /* @tp_as_buffer@ */
+  PYBUFFER(rbuf),                      /* @tp_as_buffer@ */
   Py_TPFLAGS_DEFAULT |                 /* @tp_flags@ */
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-  "A read buffer.",
+  "ReadBuffer(STR): a read buffer.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -316,9 +325,9 @@ static PyTypeObject rbuf_pytype_skel = {
   0,                                   /* @tp_weaklistoffset@ */
   0,                                   /* @tp_iter@ */
   0,                                   /* @tp_iternext@ */
-  rbuf_pymethods,                      /* @tp_methods@ */
+  PYMETHODS(rbuf),                     /* @tp_methods@ */
   0,                                   /* @tp_members@ */
-  rbuf_pygetset,                       /* @tp_getset@ */
+  PYGETSET(rbuf),                      /* @tp_getset@ */
   0,                                   /* @tp_base@ */
   0,                                   /* @tp_dict@ */
   0,                                   /* @tp_descr_get@ */
@@ -333,20 +342,29 @@ static PyTypeObject rbuf_pytype_skel = {
 
 /*----- Write buffers -----------------------------------------------------*/
 
-static void ensure(PyObject *me, size_t n)
+PyTypeObject *wbuf_pytype;
+
+int ensurebuf(PyObject *me, size_t n)
 {
   buf *b = BUF_B(me);
+  size_t nn = BSZ(b);
+  octet *p;
+  size_t want = BLEN(b) + n;
 
-  if (BLEFT(b) < n) {
-    size_t nn = BSZ(b);
-    octet *p;
-    size_t want = BLEFT(b) + n;
+  if (BLEFT(b) >= n)
+    return (0);
+  else if (BUF_LK(me))
+    BUFERR("buffer locked");
+  else {
     while (nn < want) nn <<= 1;
     p = xrealloc(BBASE(b), nn, BSZ(b));
     BCUR(b) = p + BLEN(b);
     BLIM(b) = p + nn;
     BBASE(b) = p;
+    return (0);
   }
+end:
+  return (-1);
 }
 
 static PyObject *wbuf_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
@@ -354,31 +372,41 @@ static PyObject *wbuf_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
   char *p;
   size_t n = 64;
   buf_pyobj *me = 0;
-  static char *kwlist[] = { "size", 0 };
+  static const char *const kwlist[] = { "size", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:new", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:new", KWLIST,
                                   convszt, &n))
     goto end;
   me = (buf_pyobj *)ty->tp_alloc(ty, 0);
   p = xmalloc(n);
-  me->sub = 0;
+  me->sub = 0; me->lk = 0;
   buf_init(&me->b, p, n);
 end:
   return ((PyObject *)me);
 }
 
-static int wbuf_pysegcount(PyObject *me, int *nn)
-  { if (nn) *nn = BLEN(BUF_B(me)); return (1); }
-
-static int wbuf_pyreadbuf(PyObject *me, int seg, void **q)
-  { assert(seg == 0); *q = BBASE(BUF_B(me)); return (BLEN(BUF_B(me))); }
+#ifdef PY3
+  static int wbuf_pygetbuf(PyObject *me, Py_buffer *vw, int f)
+  {
+    buf *b = BUF_B(me);
+    if (PyBuffer_FillInfo(vw, me, BBASE(b), BLEN(b), 0, f)) return (-1);
+    BUF_LK(me)++; return (0);
+  }
+  static void wbuf_pyrlsbuf(PyObject *me, Py_buffer *vw)
+    { BUF_LK(me)--; }
+#else
+  static Py_ssize_t wbuf_pysegcount(PyObject *me, Py_ssize_t *nn)
+    { if (nn) *nn = BLEN(BUF_B(me)); return (1); }
+  static Py_ssize_t wbuf_pyreadbuf(PyObject *me, Py_ssize_t seg, void **q)
+    { assert(seg == 0); *q = BBASE(BUF_B(me)); return (BLEN(BUF_B(me))); }
+#endif
 
 static PyObject *wbmeth_zero(PyObject *me, PyObject *arg)
 {
   void *p;
   size_t n;
   if (!PyArg_ParseTuple(arg, "O&:zero", convszt, &n)) return (0);
-  ensure(me, n);
+  if (ensurebuf(me, n)) return (0);
   p = buf_get(BUF_B(me), n); assert(p && BOK(BUF_B(me)));
   memset(p, 0, n);
   RETURN_ME;
@@ -386,11 +414,10 @@ static PyObject *wbmeth_zero(PyObject *me, PyObject *arg)
 
 static PyObject *wbmeth_put(PyObject *me, PyObject *arg)
 {
-  void *p;
-  int n;
-  if (!PyArg_ParseTuple(arg, "s#:put", &p, &n)) return (0);
-  ensure(me, n);
-  buf_put(BUF_B(me), p, n); assert(BOK(BUF_B(m)));
+  struct bin in;
+  if (!PyArg_ParseTuple(arg, "O&:put", convbin, &in)) return (0);
+  if (ensurebuf(me, in.sz)) return (0);
+  buf_put(BUF_B(me), in.p, in.sz); assert(BOK(BUF_B(me)));
   RETURN_ME;
 }
 
@@ -399,22 +426,25 @@ static PyObject *wbmeth_put(PyObject *me, PyObject *arg)
   {                                                                    \
     uint##n i;                                                         \
     if (!PyArg_ParseTuple(arg, "O&:putu" #w, convu##n, &i)) return (0);        \
-    ensure(me, SZ_##n);                                                        \
+    if (ensurebuf(me, SZ_##n)) return (0);                             \
     buf_putu##w(BUF_B(me), i); assert(BOK(BUF_B(me)));                 \
     RETURN_ME;                                                         \
   }
 DOUINTCONV(WBMETH_PUTU_)
 
+#define MASKz 0
 #define SZ_z 1
 #define WBMETH_PUTBLK_(n, W, w)                                                \
   static PyObject *wbmeth_putblk##w(PyObject *me, PyObject *arg)       \
   {                                                                    \
-    char *p;                                                           \
-    int sz;                                                            \
-    if (!PyArg_ParseTuple(arg, "s#:putblk" #w, &p, &sz)) return (0);   \
-    ensure(me, sz + SZ_##n);                                           \
-    buf_putmem##w(BUF_B(me), p, sz); assert(BOK(BUF_B(me)));           \
+    struct bin in;                                                     \
+    if (!PyArg_ParseTuple(arg, "O&:putblk" #w, convbin, &in)) goto end;        \
+    if (MASK##W && in.sz > MASK##W) VALERR("too large");               \
+    if (ensurebuf(me, in.sz + SZ_##n)) return (0);                     \
+    buf_putmem##w(BUF_B(me), in.p, in.sz); assert(BOK(BUF_B(me)));     \
     RETURN_ME;                                                         \
+  end:                                                                 \
+    return (0);                                                                \
   }
 BUF_DOSUFFIXES(WBMETH_PUTBLK_)
 
@@ -422,7 +452,7 @@ static PyObject *wbmeth_putmp(PyObject *me, PyObject *arg)
 {
   mp *x = 0;
   if (!PyArg_ParseTuple(arg, "O&:putmp", convmp, &x)) return (0);
-  ensure(me, mp_octets(x) + 2);
+  if (ensurebuf(me, mp_octets(x) + 2)) return (0);
   buf_putmp(BUF_B(me), x); assert(BOK(BUF_B(me)));
   RETURN_ME;
 }
@@ -431,7 +461,7 @@ static PyObject *wbmeth_putgf(PyObject *me, PyObject *arg)
 {
   mp *x = 0;
   if (!PyArg_ParseTuple(arg, "O&:putgf", convgf, &x)) return (0);
-  ensure(me, mp_octets(x) + 2);
+  if (ensurebuf(me, mp_octets(x) + 2)) return (0);
   buf_putmp(BUF_B(me), x); assert(BOK(BUF_B(me)));
   MP_DROP(x);
   RETURN_ME;
@@ -441,8 +471,9 @@ static PyObject *wbmeth_putecpt(PyObject *me, PyObject *arg)
 {
   ec pt = EC_INIT;
   if (!PyArg_ParseTuple(arg, "O&:putecpt", convecpt, &pt)) return (0);
-  if (EC_ATINF(&pt)) ensure(me, 2);
-  else ensure(me, 4 + mp_octets(pt.x) + mp_octets(pt.y));
+  if (ensurebuf(me, EC_ATINF(&pt) ? 2 :
+               6 + mp_octets(pt.x) + mp_octets(pt.y)))
+    return (0);
   buf_putec(BUF_B(me), &pt); assert(BOK(BUF_B(me)));
   EC_DESTROY(&pt);
   RETURN_ME;
@@ -451,21 +482,42 @@ static PyObject *wbmeth_putecpt(PyObject *me, PyObject *arg)
 static PyObject *wbmeth_putecptraw(PyObject *me, PyObject *arg)
 {
   PyObject *ptobj;
+  ec_curve *cc;
   ec pt = EC_INIT;
   if (!PyArg_ParseTuple(arg, "O!:putecptraw", ecptcurve_pytype, &ptobj))
     return (0);
-  EC_OUT(ECPT_C(ptobj), &pt, ECPT_P(ptobj));
-  ensure(me, ECPT_C(ptobj)->f->noctets * 2 + 1);
-  ec_putraw(ECPT_C(ptobj), BUF_B(me), &pt); assert(BOK(BUF_B(me)));
+  cc = ECPT_C(ptobj);
+  EC_OUT(cc, &pt, ECPT_P(ptobj));
+  if (ensurebuf(me, 2*cc->f->noctets + 1)) return (0);
+  ec_putraw(cc, BUF_B(me), &pt); assert(BOK(BUF_B(me)));
   EC_DESTROY(&pt);
   RETURN_ME;
 }
 
+static PyObject *wbmeth_ec2osp(PyObject *me, PyObject *arg, PyObject *kw)
+{
+  PyTypeObject *ptobj;
+  ec_curve *cc;
+  ec pt = EC_INIT;
+  unsigned f = EC_EXPLY;
+  static const char *const kwlist[] = { "point", "flags", 0 };
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!|O&:os2ecp", KWLIST,
+                                  ecptcurve_pytype, &ptobj, convuint, &f))
+    goto end;
+  cc = ECPT_C(ptobj);
+  EC_OUT(cc, &pt, ECPT_P(ptobj));
+  if (ensurebuf(me, 2*cc->f->noctets + 1)) return (0);
+  if (ec_ec2osp(cc, f, BUF_B(me), &pt)) VALERR("invalid flags");
+  RETURN_ME;
+end:
+  return (0);
+}
+
 static PyObject *wbmeth_putge(PyObject *me, PyObject *arg)
 {
   PyObject *geobj;
   if (!PyArg_ParseTuple(arg, "O!:putge", ge_pytype, &geobj)) return (0);
-  ensure(me, GE_G(geobj)->noctets);
+  if (ensurebuf(me, GE_G(geobj)->noctets)) return (0);
   G_TOBUF(GE_G(geobj), BUF_B(me), GE_X(geobj)); assert(BOK(BUF_B(me)));
   RETURN_ME;
 }
@@ -474,7 +526,7 @@ static PyObject *wbmeth_putgeraw(PyObject *me, PyObject *arg)
 {
   PyObject *geobj;
   if (!PyArg_ParseTuple(arg, "O!:putgeraw", ge_pytype, &geobj)) return (0);
-  ensure(me, GE_G(geobj)->noctets);
+  if (ensurebuf(me, GE_G(geobj)->noctets)) return (0);
   G_TORAW(GE_G(geobj), BUF_B(me), GE_X(geobj)); assert(BOK(BUF_B(me)));
   RETURN_ME;
 }
@@ -482,43 +534,53 @@ static PyObject *wbmeth_putgeraw(PyObject *me, PyObject *arg)
 static PyObject *wbget_size(PyObject *me, void *hunoz)
   { return (PyInt_FromLong(BLEN(BUF_B(me)))); }
 
-static PyGetSetDef wbuf_pygetset[] = {
+static PyObject *wbget_contents(PyObject *me, void *hunoz)
+  { return (bytestring_pywrap(BBASE(BUF_B(me)), BLEN(BUF_B(me)))); }
+
+static const PyGetSetDef wbuf_pygetset[] = {
 #define GETSETNAME(op, name) wb##op##_##name
-  GET  (size,                  "WBUF.size -> SIZE")
+  GET  (size,          "WBUF.size -> SIZE")
+  GET  (contents,      "WBUF.contents -> STR")
 #undef GETSETNAME
   { 0 }
 };
 
-static PyMethodDef wbuf_pymethods[] = {
+static const PyMethodDef wbuf_pymethods[] = {
 #define METHNAME(func) wbmeth_##func
-  METH (zero,                  "WBUF.skip(N)")
-  METH (put,                   "WBUF.put(BYTES)")
+  METH (zero,          "WBUF.zero(N)")
+  METH (put,           "WBUF.put(BYTES)")
 #define WBMETH_DECL_PUTU_(n, W, w)                                     \
-    METH(putu##w, "WBUF.putu" #w "(INT)")
+    METH(putu##w,      "WBUF.putu" #w "(INT)")
   DOUINTCONV(WBMETH_DECL_PUTU_)
 #define WBMETH_DECL_PUTBLK_(n, W, w)                                   \
-    METH(putblk##w, "WBUF.putblk" #w "(BYTES)")
+    METH(putblk##w,    "WBUF.putblk" #w "(BYTES)")
   BUF_DOSUFFIXES(WBMETH_DECL_PUTBLK_)
-  METH (putmp,                 "WBUF.putmp(X)")
-  METH (putgf,                 "WBUF.putgf(X)")
-  KWMETH(putecpt,              "WBUF.putecpt(P)")
-  METH (putecptraw,            "WBUF.putecptraw(P)")
-  METH (putge,                 "WBUF.putge(X)")
-  METH (putgeraw,              "WBUF.putgeraw(X)")
+  METH (putmp,         "WBUF.putmp(X)")
+  METH (putgf,         "WBUF.putgf(X)")
+  METH (putecpt,       "WBUF.putecpt(P)")
+  METH (putecptraw,    "WBUF.putecptraw(P)")
+  KWMETH(ec2osp,       "WBUF.ec2osp(P, [flags = EC_EXPLY])")
+  METH (putge,         "WBUF.putge(X)")
+  METH (putgeraw,      "WBUF.putgeraw(X)")
 #undef METHNAME
   { 0 }
 };
 
-static PyBufferProcs wbuf_pybuffer = {
+static const PyBufferProcs wbuf_pybuffer = {
+#ifdef PY3
+  wbuf_pygetbuf,                       /* @bf_getbuffer@ */
+  wbuf_pyrlsbuf                                /* @bf_releasebuffer@ */
+#else
   wbuf_pyreadbuf,                      /* @bf_getreadbuffer@ */
   0,                                   /* @bf_getwritebuffer@ */
   wbuf_pysegcount,                     /* @bf_getsegcount@ */
   0                                    /* @bf_getcharbuffer@ */
+#endif
 };
 
-static PyTypeObject wbuf_pytype_skel = {
-  PyObject_HEAD_INIT(0) 0,             /* Header */
-  "catacomb.WriteBuffer",              /* @tp_name@ */
+static const PyTypeObject wbuf_pytype_skel = {
+  PyVarObject_HEAD_INIT(0, 0)          /* Header */
+  "WriteBuffer",                       /* @tp_name@ */
   sizeof(buf_pyobj),                   /* @tp_basicsize@ */
   0,                                   /* @tp_itemsize@ */
 
@@ -536,12 +598,12 @@ static PyTypeObject wbuf_pytype_skel = {
   0,                                   /* @tp_str@ */
   0,                                   /* @tp_getattro@ */
   0,                                   /* @tp_setattro@ */
-  &wbuf_pybuffer,                      /* @tp_as_buffer@ */
+  PYBUFFER(wbuf),                      /* @tp_as_buffer@ */
   Py_TPFLAGS_DEFAULT |                 /* @tp_flags@ */
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-  "A write buffer.",
+  "WriteBuffer([size = ?]): a write buffer.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -549,9 +611,9 @@ static PyTypeObject wbuf_pytype_skel = {
   0,                                   /* @tp_weaklistoffset@ */
   0,                                   /* @tp_iter@ */
   0,                                   /* @tp_iternext@ */
-  wbuf_pymethods,                      /* @tp_methods@ */
+  PYMETHODS(wbuf),                     /* @tp_methods@ */
   0,                                   /* @tp_members@ */
-  wbuf_pygetset,                       /* @tp_getset@ */
+  PYGETSET(wbuf),                      /* @tp_getset@ */
   0,                                   /* @tp_base@ */
   0,                                   /* @tp_dict@ */
   0,                                   /* @tp_descr_get@ */
@@ -566,6 +628,8 @@ static PyTypeObject wbuf_pytype_skel = {
 
 /*----- Initialization ----------------------------------------------------*/
 
+PyObject *buferr;
+
 void buffer_pyinit(void)
 {
   INITTYPE(rbuf, root);