[Python-checkins] r46177 - in python/branches/blais-bytebuf: Lib/test/test_hotbuf.py Modules/hotbufmodule.c

martin.blais python-checkins at python.org
Wed May 24 16:56:27 CEST 2006


Author: martin.blais
Date: Wed May 24 16:56:27 2006
New Revision: 46177

Added:
   python/branches/blais-bytebuf/Lib/test/test_hotbuf.py   (contents, props changed)
Modified:
   python/branches/blais-bytebuf/Modules/hotbufmodule.c
Log:
Added basic java buffer operations on hotbuf

Added: python/branches/blais-bytebuf/Lib/test/test_hotbuf.py
==============================================================================
--- (empty file)
+++ python/branches/blais-bytebuf/Lib/test/test_hotbuf.py	Wed May 24 16:56:27 2006
@@ -0,0 +1,81 @@
+# Test the hotbuf module.
+#
+# $Id$
+#
+#  Copyright (C) 2005   Gregory P. Smith (greg at electricrain.com)
+#  Licensed to PSF under a Contributor Agreement.
+#
+
+from hotbuf import hotbuf
+import unittest
+from test import test_support
+
+
+class HotbufTestCase(unittest.TestCase):
+
+    def test_base( self ):
+        CAPACITY = 1024
+
+        # Create a new hotbuf
+        self.assertRaises(ValueError, hotbuf, -1)
+        self.assertRaises(ValueError, hotbuf, 0)
+        b = hotbuf(CAPACITY)
+        self.assertEquals(len(b), CAPACITY)
+        self.assertEquals(b.capacity(), CAPACITY)
+        
+        # Play with the position
+        assert b.position() == 0
+        b.setposition(10)
+        self.assertEquals(b.position(), 10)
+        self.assertRaises(IndexError, b.setposition, CAPACITY + 1)
+
+        # Play with the limit
+        assert b.limit() == CAPACITY
+        b.setlimit(CAPACITY - 10)
+        self.assertEquals(b.limit(), CAPACITY - 10)
+        self.assertRaises(IndexError, b.setlimit, CAPACITY + 1)
+        b.setlimit(b.position() - 1)
+        self.assertEquals(b.position(), b.limit())
+
+        # Play with reset before the mark has been set.
+        self.assertRaises(IndexError, b.setlimit, CAPACITY + 1)
+        
+        # Play with the mark
+        b.setposition(10)
+        b.setlimit(100)
+        b.setmark()
+        b.setposition(15)
+        self.assertEquals(b.mark(), 10)
+
+        # Play with clear
+        b.clear()
+        self.assertEquals((b.position(), b.limit(), b.mark()),
+                          (0, CAPACITY, -1))
+
+        # Play with flip.
+        b.setposition(42)
+        b.setlimit(104)
+        b.setmark()
+        b.flip()
+        self.assertEquals((b.position(), b.limit(), b.mark()),
+                          (0, 42, -1))
+        
+        # Play with rewind.
+        b.setposition(42)
+        b.setlimit(104)
+        b.setmark()
+        b.rewind()
+        self.assertEquals((b.position(), b.limit(), b.mark()),
+                          (0, 104, -1))
+
+        # Play with remaining.
+        self.assertEquals(b.remaining(), 104)
+
+        
+def test_main():
+    test_support.run_unittest(HotbufTestCase)
+
+
+if __name__ == "__main__":
+    test_main()
+

Modified: python/branches/blais-bytebuf/Modules/hotbufmodule.c
==============================================================================
--- python/branches/blais-bytebuf/Modules/hotbufmodule.c	(original)
+++ python/branches/blais-bytebuf/Modules/hotbufmodule.c	Wed May 24 16:56:27 2006
@@ -28,13 +28,37 @@
 #endif /* !Py_HOTBUFOBJECT_H */
 
 
+
 /* ===========================================================================
- * Byte Buffer object implementation 
+ * Byte Buffer object implementation
  */
 
 
 /*
  * hotbuf object structure declaration.
+
+   From the Java Buffer docs:
+
+     A buffer is a linear, finite sequence of elements of a specific
+     primitive type. Aside from its content, the essential properties of a
+     buffer are its capacity, limit, and position:
+
+       A buffer's capacity is the number of elements it contains. The
+       capacity of a buffer is never negative and never changes.
+
+       A buffer's limit is the index of the first element that should not
+       be read or written. A buffer's limit is never negative and is never
+       greater than its capacity.
+
+       A buffer's position is the index of the next element to be read or
+       written. A buffer's position is never negative and is never greater
+       than its limit.
+
+   The following invariant holds for the mark, position, limit, and
+   capacity values:
+
+      0 <= mark <= position <= limit <= capacity (length)
+
  */
 typedef struct {
     PyObject_HEAD
@@ -44,7 +68,31 @@
 
     /* Total size in bytes of the area that we can access.  The allocated
        memory must be at least as large as this size. */
-    Py_ssize_t b_size;
+    Py_ssize_t b_capacity;
+
+    /*
+     * The "active window" is defined by the interval [position, limit[.
+     */
+      
+    /* The current position in the buffer. */
+    int b_position;
+
+    /* The limit position in the buffer. */
+    int b_limit;
+
+    /* The mark. From the Java Buffer docs:
+
+         A buffer's mark is the index to which its position will be reset when
+         the reset method is invoked. The mark is not always defined, but when
+         it is defined it is never negative and is never greater than the
+         position. If the mark is defined then it is discarded when the
+         position or the limit is adjusted to a value smaller than the mark. If
+         the mark is not defined then invoking the reset method causes an
+         InvalidMarkException to be thrown.
+
+       The mark is set to -1 to indicate that the mark is unset.
+    */
+    int b_mark;
 
 } PyHotbufObject;
 
@@ -58,26 +106,29 @@
  * Create a new hotbuf where we allocate the memory ourselves.
  */
 PyObject *
-PyHotbuf_New(Py_ssize_t size)
+PyHotbuf_New(Py_ssize_t capacity)
 {
     PyObject *o;
     PyHotbufObject * b;
 
-    if (size < 0) {
+    if (capacity < 0) {
         PyErr_SetString(PyExc_ValueError,
-                        "size must be zero or positive");
+                        "capacity must be zero or positive");
         return NULL;
     }
 
     /* FIXME: check for overflow in multiply */
-    o = (PyObject *)PyObject_MALLOC(sizeof(*b) + size);
+    o = (PyObject *)PyObject_MALLOC(sizeof(*b) + capacity);
     if ( o == NULL )
         return PyErr_NoMemory();
     b = (PyHotbufObject *) PyObject_INIT(o, &PyHotbuf_Type);
 
     /* We setup the memory buffer to be right after the object itself. */
     b->b_ptr = (void *)(b + 1);
-    b->b_size = size;
+    b->b_position = 0;
+    b->b_mark = -1;
+    b->b_limit = capacity;
+    b->b_capacity = capacity;
 
     return o;
 }
@@ -94,12 +145,12 @@
 
     if (!_PyArg_NoKeywords("hotbuf()", kw))
         return NULL;
- 
+
     if (!PyArg_ParseTuple(args, "n:hotbuf", &size))
         return NULL;
 
     if ( size <= 0 ) {
-        PyErr_SetString(PyExc_TypeError,
+        PyErr_SetString(PyExc_ValueError,
                         "size must be greater than zero");
         return NULL;
     }
@@ -118,27 +169,29 @@
     PyObject_DEL(self);
 }
 
-/* 
- * Comparison.
+/*
+ * Comparison.  We compare the active windows, not the entire allocated buffer
+ * memory.
  */
 static int
 hotbuf_compare(PyHotbufObject *self, PyHotbufObject *other)
 {
     Py_ssize_t min_len;
     int cmp;
-
-    min_len = (self->b_size < other->b_size) ? self->b_size : other->b_size;
+    
+    min_len = ((self->b_capacity < other->b_capacity) ?
+               self->b_capacity : other->b_capacity);
     if (min_len > 0) {
         cmp = memcmp(self->b_ptr, other->b_ptr, min_len);
         if (cmp != 0)
             return cmp;
     }
-    return ((self->b_size < other->b_size) ? 
-            -1 : (self->b_size > other->b_size) ? 1 : 0);
+    return ((self->b_capacity < other->b_capacity) ?
+            -1 : (self->b_capacity > other->b_capacity) ? 1 : 0);
 }
 
 
-/* 
+/*
  * Conversion to 'repr' string.
  */
 static PyObject *
@@ -146,21 +199,276 @@
 {
     return PyString_FromFormat("<hotbuf ptr %p, size %zd at %p>",
                                self->b_ptr,
-                               self->b_size,
+                               self->b_capacity,
                                self);
 }
 
-/* 
- * Conversion to string.
+/*
+ * Conversion to string.  We convert only the active window.
  */
 static PyObject *
 hotbuf_str(PyHotbufObject *self)
 {
-    return PyString_FromStringAndSize((const char *)self->b_ptr, self->b_size);
+    return PyString_FromStringAndSize((const char *)self->b_ptr, self->b_capacity);
+}
+
+
+
+/* ===========================================================================
+ * Object Methods
+ */
+
+PyDoc_STRVAR(capacity__doc__,
+"B.capacity() -> int\n\
+\n\
+Returns this buffer's capacity. \n\
+(the entire size of the allocated buffer.)");
+
+static PyObject*
+hotbuf_capacity(PyHotbufObject *self)
+{
+    return PyInt_FromLong(self->b_capacity);
+}
+
+
+PyDoc_STRVAR(position__doc__,
+"B.position() -> int\n\
+\n\
+Returns this buffer's position.");
+
+static PyObject*
+hotbuf_position(PyHotbufObject *self)
+{
+    return PyInt_FromLong(self->b_position);
+}
+
+
+PyDoc_STRVAR(setposition__doc__,
+"B.setposition(int)\n\
+\n\
+Sets this buffer's position. If the mark is defined and larger than\n\
+the new position then it is discarded.  If the given position is\n\
+larger than the limit an exception is raised.");
+
+static PyObject*
+hotbuf_setposition(PyHotbufObject *self, PyObject* arg)
+{
+    int newposition;
+
+    newposition = PyInt_AsLong(arg);
+    if (newposition == -1 && PyErr_Occurred())
+        return NULL;
+
+    if ( newposition > self->b_capacity ) {
+        PyErr_SetString(PyExc_IndexError,
+                        "position must be smaller than capacity");
+        return NULL;
+    }
+
+    /* Set the new position */
+    self->b_position = newposition;
+
+    /* Discard the mark if it is beyond the new position */
+    if ( self->b_mark > self->b_position ) 
+        self->b_mark = -1;
+
+    return Py_None;
+}
+
+
+PyDoc_STRVAR(limit__doc__,
+"B.limit() -> int\n\
+\n\
+Returns this buffer's limit.");
+
+static PyObject*
+hotbuf_limit(PyHotbufObject *self)
+{
+    return PyInt_FromLong(self->b_limit);
+}
+
+
+PyDoc_STRVAR(setlimit__doc__,
+"B.setlimit(int)\n\
+\n\
+Sets this buffer's limit. If the position is larger than the new limit\n\
+then it is set to the new limit. If the mark is defined and larger\n\
+than the new limit then it is discarded.");
+
+static PyObject*
+hotbuf_setlimit(PyHotbufObject *self, PyObject* arg)
+{
+    int newlimit;
+
+    newlimit = PyInt_AsLong(arg);
+    if (newlimit == -1 && PyErr_Occurred())
+        return NULL;
+
+    if ( newlimit > self->b_capacity ) {
+        PyErr_SetString(PyExc_IndexError,
+                        "limit must be smaller than capacity");
+        return NULL;
+    }
+
+    /* Set the new limit. */
+    self->b_limit = newlimit;
+
+    /* If the position is larger than the new limit, set it to the new
+       limit. */
+    if ( self->b_position > self->b_limit )
+        self->b_position = newlimit;
+
+    /* Discard the mark if it is beyond the new limit */
+    if ( self->b_mark > self->b_position ) 
+        self->b_mark = -1;
+
+    return Py_None;
+}
+
+
+PyDoc_STRVAR(mark__doc__,
+"B.mark() -> int\n\
+\n\
+Returns this buffer's mark. \n\
+Return -1 if the mark is not set.");
+
+static PyObject*
+hotbuf_mark(PyHotbufObject *self)
+{
+    return PyInt_FromLong(self->b_mark);
+}
+
+
+PyDoc_STRVAR(setmark__doc__,
+"B.setmark()\n\
+\n\
+Sets this buffer's mark at its position.");
+
+static PyObject*
+hotbuf_setmark(PyHotbufObject *self)
+{
+    self->b_mark = self->b_position;
+    return Py_None;
+}
+
+
+PyDoc_STRVAR(reset__doc__,
+"B.reset() -> int\n\
+\n\
+Resets this buffer's position to the previously-marked position.\n\
+Invoking this method neither changes nor discards the mark's value.\n\
+An IndexError is raised if the mark has not been set.\n\
+This method returns the new position's value.");
+
+static PyObject*
+hotbuf_reset(PyHotbufObject *self)
+{
+    if ( self->b_mark == -1 ) {
+        PyErr_SetString(PyExc_IndexError,
+                        "mark has not been yet set");
+        return NULL;
+    }
+
+    self->b_position = self->b_mark;
+    return PyInt_FromLong(self->b_position);
+}
+
+
+PyDoc_STRVAR(clear__doc__,
+"B.clear()\n\
+\n\
+Clears this buffer. The position is set to zero, the limit is set to\n\
+the capacity, and the mark is discarded.\n\
+\n\
+Invoke this method before using a sequence of channel-read or put\n\
+operations to fill this buffer. For example:\n\
+\n\
+     buf.clear()     # Prepare buffer for reading\n\
+     in.read(buf)    # Read data\n\
+\n\
+(This method does not actually erase the data in the buffer, but it is\n\
+named as if it did because it will most often be used in situations in\n\
+which that might as well be the case.)");
+
+static PyObject*
+hotbuf_clear(PyHotbufObject *self)
+{
+    self->b_position = 0;
+    self->b_limit = self->b_capacity;
+    self->b_mark = -1;
+    return Py_None;
 }
 
 
-/* Hotbuf methods */
+PyDoc_STRVAR(flip__doc__,
+"B.flip()\n\
+\n\
+Flips this buffer. The limit is set to the current position and then\n\
+the position is set to zero. If the mark is defined then it is\n\
+discarded.\n\
+\n\
+After a sequence of channel-read or put operations, invoke this method\n\
+to prepare for a sequence of channel-write or relative get\n\
+operations. For example:\n\
+\n\
+     buf.put(magic)    # Prepend header\n\
+     in.read(buf)      # Read data into rest of buffer\n\
+     buf.flip()        # Flip buffer\n\
+     out.write(buf)    # Write header + data to channel\n\
+\n\
+This method is often used in conjunction with the compact method when\n\
+transferring data from one place to another.");
+
+static PyObject*
+hotbuf_flip(PyHotbufObject *self)
+{
+    self->b_limit = self->b_position;
+    self->b_position = 0;
+    self->b_mark = -1;
+    return Py_None;
+}
+
+
+PyDoc_STRVAR(rewind__doc__,
+"B.rewind()\n\
+\n\
+Rewinds this buffer. The position is set to zero and the mark is\n\
+discarded.\n\
+\n\
+Invoke this method before a sequence of channel-write or get\n\
+operations, assuming that the limit has already been set\n\
+appropriately. For example:\n\
+\n\
+     out.write(buf)    # Write remaining data\n\
+     buf.rewind()      # Rewind buffer\n\
+     buf.get(array)    # Copy data into array\n\
+");
+
+static PyObject*
+hotbuf_rewind(PyHotbufObject *self)
+{
+    self->b_position = 0;
+    self->b_mark = -1;
+    return Py_None;
+}
+
+
+PyDoc_STRVAR(remaining__doc__,
+"B.remaining() -> int\n\
+\n\
+Returns the number of bytes between the current position and the limit.");
+
+static PyObject*
+hotbuf_remaining(PyHotbufObject *self)
+{
+    return PyInt_FromLong(self->b_limit - self->b_position);
+}
+
+
+
+/* ===========================================================================
+ * Buffer protocol methods 
+ */
 
 /*
  * Returns the buffer for reading or writing.
@@ -175,14 +483,14 @@
     }
 
     *pp = self->b_ptr;
-    return self->b_size;
+    return self->b_capacity;
 }
 
 static Py_ssize_t
 hotbuf_getsegcount(PyHotbufObject *self, Py_ssize_t *lenp)
 {
     if (lenp)
-        *lenp = self->b_size;
+        *lenp = self->b_capacity;
     return 1;
 }
 
@@ -196,22 +504,26 @@
     }
 
     *pp = (const char *)self->b_ptr;
-    return self->b_size;
+    return self->b_capacity;
 }
 
+
+
 /* ===========================================================================
- * Sequence methods 
+ * Sequence methods
  */
 
 static Py_ssize_t
 hotbuf_length(PyHotbufObject *self)
 {
-    return self->b_size;
+    assert(self->b_position <= self->b_limit);
+    return self->b_limit - self->b_position;
 }
 
 
+
 /* ===========================================================================
- * Object interfaces declaration 
+ * Object interfaces declaration
  */
 
 /* FIXME: needs an update */
@@ -236,6 +548,23 @@
 ");
 
 
+static PyMethodDef
+hotbuf_methods[] = {
+	{"clear", (PyCFunction)hotbuf_clear, METH_NOARGS, clear__doc__},
+	{"capacity", (PyCFunction)hotbuf_capacity, METH_NOARGS, capacity__doc__},
+	{"position", (PyCFunction)hotbuf_position, METH_NOARGS, position__doc__},
+	{"setposition", (PyCFunction)hotbuf_setposition, METH_O, setposition__doc__},
+	{"limit", (PyCFunction)hotbuf_limit, METH_NOARGS, limit__doc__},
+	{"setlimit", (PyCFunction)hotbuf_setlimit, METH_O, setlimit__doc__},
+	{"mark", (PyCFunction)hotbuf_mark, METH_NOARGS, mark__doc__},
+	{"setmark", (PyCFunction)hotbuf_setmark, METH_NOARGS, setmark__doc__},
+	{"reset", (PyCFunction)hotbuf_reset, METH_NOARGS, reset__doc__},
+	{"flip", (PyCFunction)hotbuf_flip, METH_NOARGS, flip__doc__},
+	{"rewind", (PyCFunction)hotbuf_rewind, METH_NOARGS, rewind__doc__},
+	{"remaining", (PyCFunction)hotbuf_remaining, METH_NOARGS, remaining__doc__},
+	{NULL, NULL} /* sentinel */
+};
+
 static PySequenceMethods hotbuf_as_sequence = {
         (lenfunc)hotbuf_length,                 /*sq_length*/
         0 /* (binaryfunc)hotbuf_concat */,              /*sq_concat*/
@@ -259,12 +588,12 @@
     "hotbuf",
     sizeof(PyHotbufObject),
     0,
-    (destructor)hotbuf_dealloc,        /* tp_dealloc */
+    (destructor)hotbuf_dealloc,         /* tp_dealloc */
     0,                                  /* tp_print */
     0,                                  /* tp_getattr */
     0,                                  /* tp_setattr */
-    (cmpfunc)hotbuf_compare,           /* tp_compare */
-    (reprfunc)hotbuf_repr,             /* tp_repr */
+    (cmpfunc)hotbuf_compare,            /* tp_compare */
+    (reprfunc)hotbuf_repr,              /* tp_repr */
     0,                                  /* tp_as_number */
     &hotbuf_as_sequence,               /* tp_as_sequence */
     0,                                  /* tp_as_mapping */
@@ -282,7 +611,7 @@
     0,                                  /* tp_weaklistoffset */
     0,                                  /* tp_iter */
     0,                                  /* tp_iternext */
-    0,                                  /* tp_methods */        
+    hotbuf_methods,                     /* tp_methods */
     0,                                  /* tp_members */
     0,                                  /* tp_getset */
     0,                                  /* tp_base */
@@ -296,6 +625,7 @@
 };
 
 
+
 /* ===========================================================================
  * Install Module
  */
@@ -345,13 +675,14 @@
 
 
 
-/* 
+/*
    TODO
    ----
    - Update doc.
    - Add hash function
    - Add support for sequence methods.
-
+   - Perhaps implement returning the buffer object itself from some of
+     the methods in order to allow chaining of operations on a single line.
 
    Pending Issues
    --------------


More information about the Python-checkins mailing list