[Python-checkins] r46236 - in python/branches/blais-bytebuf: Lib/hotbuf.py Lib/test/test_hotbuf.py Lib/test/test_struct.py Modules/_hotbuf.c Modules/_struct.c

martin.blais python-checkins at python.org
Thu May 25 20:25:28 CEST 2006


Author: martin.blais
Date: Thu May 25 20:25:26 2006
New Revision: 46236

Modified:
   python/branches/blais-bytebuf/Lib/hotbuf.py
   python/branches/blais-bytebuf/Lib/test/test_hotbuf.py
   python/branches/blais-bytebuf/Lib/test/test_struct.py
   python/branches/blais-bytebuf/Modules/_hotbuf.c
   python/branches/blais-bytebuf/Modules/_struct.c
Log:
Added struct.pack_to implementation.

Modified: python/branches/blais-bytebuf/Lib/hotbuf.py
==============================================================================
--- python/branches/blais-bytebuf/Lib/hotbuf.py	(original)
+++ python/branches/blais-bytebuf/Lib/hotbuf.py	Thu May 25 20:25:26 2006
@@ -10,15 +10,5 @@
 _long = Struct('l')
 
 class hotbuf(_hotbuf):
-
-    def getlong( self ):
-        r = _long.unpack_from(self, 0)
-        self.setposition(self.position + _long.size)
-        return r
-
-##     def putlong( self ):
-##         s = _long.pack(0)
-##         self.setposition(self.position + _long.size)
-##         return 
-
+    pass
 

Modified: python/branches/blais-bytebuf/Lib/test/test_hotbuf.py
==============================================================================
--- python/branches/blais-bytebuf/Lib/test/test_hotbuf.py	(original)
+++ python/branches/blais-bytebuf/Lib/test/test_hotbuf.py	Thu May 25 20:25:26 2006
@@ -7,13 +7,16 @@
 #
 
 from hotbuf import hotbuf
+from struct import Struct
 import unittest
 from test import test_support
 
 
 CAPACITY = 1024
 MSG = 'Martin Blais was here scribble scribble.'
-
+# Note: we don't use floats because comparisons will cause precision errors due
+# to the binary conversion.
+fmt = Struct('llci')
 
 class HotbufTestCase(unittest.TestCase):
 
@@ -75,6 +78,13 @@
         b.setposition(10)
         self.assertEquals(b.remaining(), 94)
 
+        # Play with advance.
+        self.assertEquals(b.position, 10)
+        b.advance(32)
+        self.assertEquals(b.position, 42)
+
+        self.assertRaises(IndexError, b.advance, CAPACITY)
+
     def test_compact( self ):
         b = hotbuf(CAPACITY)
 
@@ -137,9 +147,28 @@
     def test_compare( self ):
         b = hotbuf(CAPACITY)
 
-## FIXME we need a few methods to be able to write strings into and out of it
+    def test_pack( self ):
+        ARGS = 42, 16, '@', 3
+        # Pack to a string.
+        s = fmt.pack(*ARGS)
 
+        # Pack directly into the buffer and compare the strings.
+        b = hotbuf(CAPACITY)
+        fmt.pack_to(b, 0, *ARGS)
+        b.setlimit(len(s))
+        self.assertEquals(str(b), s)
 
+    def test_unpack( self ):
+        ARGS = 42, 16, '@', 3
+        b = hotbuf(CAPACITY)
+
+        # Pack normally and put that string in the buffer.
+        s = fmt.pack(*ARGS)
+        b.putstr(s)
+
+        # Unpack directly from the buffer and compare.
+        b.flip()
+        self.assertEquals(fmt.unpack_from(b), ARGS)
 
 
 def test_main():

Modified: python/branches/blais-bytebuf/Lib/test/test_struct.py
==============================================================================
--- python/branches/blais-bytebuf/Lib/test/test_struct.py	(original)
+++ python/branches/blais-bytebuf/Lib/test/test_struct.py	Thu May 25 20:25:26 2006
@@ -1,5 +1,8 @@
 from test.test_support import TestFailed, verbose, verify
+import test.test_support
 import struct
+import array
+import unittest
 
 import sys
 ISBIGENDIAN = sys.byteorder == "big"
@@ -438,31 +441,6 @@
 
 test_705836()
 
-def test_unpack_from():
-    test_string = 'abcd01234'
-    fmt = '4s'
-    s = struct.Struct(fmt)
-    for cls in (str, buffer):
-        data = cls(test_string)
-        assert s.unpack_from(data) == ('abcd',)
-        assert s.unpack_from(data, 2) == ('cd01',)
-        assert s.unpack_from(data, 4) == ('0123',)
-        for i in xrange(6):
-            assert s.unpack_from(data, i) == (data[i:i+4],)
-        for i in xrange(6, len(test_string) + 1):
-            simple_err(s.unpack_from, data, i)
-    for cls in (str, buffer):
-        data = cls(test_string)
-        assert struct.unpack_from(fmt, data) == ('abcd',)
-        assert struct.unpack_from(fmt, data, 2) == ('cd01',)
-        assert struct.unpack_from(fmt, data, 4) == ('0123',)
-        for i in xrange(6):
-            assert struct.unpack_from(fmt, data, i) == (data[i:i+4],)
-        for i in xrange(6, len(test_string) + 1):
-            simple_err(struct.unpack_from, fmt, data, i)
-
-test_unpack_from()
-
 def test_1229380():
     for endian in ('', '>', '<'):
         for cls in (int, long):
@@ -478,3 +456,60 @@
 if 0:
     # TODO: bug #1229380
     test_1229380()
+
+class PackBufferTestCase(unittest.TestCase):
+    """
+    Test the packing methods that work on buffers.
+    """
+
+    def test_unpack_from( self ):
+        test_string = 'abcd01234'
+        fmt = '4s'
+        s = struct.Struct(fmt)
+        for cls in (str, buffer):
+            data = cls(test_string)
+            self.assertEquals(s.unpack_from(data), ('abcd',))
+            self.assertEquals(s.unpack_from(data, 2), ('cd01',))
+            self.assertEquals(s.unpack_from(data, 4), ('0123',))
+            for i in xrange(6):
+                self.assertEquals(s.unpack_from(data, i), (data[i:i+4],))
+            for i in xrange(6, len(test_string) + 1):
+                simple_err(s.unpack_from, data, i)
+        for cls in (str, buffer):
+            data = cls(test_string)
+            self.assertEquals(struct.unpack_from(fmt, data), ('abcd',))
+            self.assertEquals(struct.unpack_from(fmt, data, 2), ('cd01',))
+            self.assertEquals(struct.unpack_from(fmt, data, 4), ('0123',))
+            for i in xrange(6):
+                self.assertEquals(struct.unpack_from(fmt, data, i),
+                                  (data[i:i+4],))
+            for i in xrange(6, len(test_string) + 1):
+                simple_err(struct.unpack_from, fmt, data, i)
+
+    def test_pack_to( self ):
+        test_string = 'Reykjavik rocks, eow!'
+        writable_buf = array.array('c', ' '*100)
+        fmt = '21s'
+        s = struct.Struct(fmt)
+
+        # Test without offset
+        s.pack_to(writable_buf, 0, test_string)
+        from_buf = writable_buf.tostring()[:len(test_string)]
+        self.assertEquals(from_buf, test_string)
+
+        # Test with offset.
+        s.pack_to(writable_buf, 10, test_string)
+        from_buf = writable_buf.tostring()[:len(test_string)+10]
+        self.assertEquals(from_buf, (test_string[:10] + test_string))
+
+        # Go beyond boundaries.
+        small_buf = array.array('c', ' '*10)
+        self.assertRaises(struct.error, s.pack_to, small_buf, 0, test_string)
+        self.assertRaises(struct.error, s.pack_to, small_buf, 2, test_string)
+
+def test_main():
+    test.test_support.run_unittest(PackBufferTestCase)
+    
+if __name__ == "__main__":
+    test_main()
+

Modified: python/branches/blais-bytebuf/Modules/_hotbuf.c
==============================================================================
--- python/branches/blais-bytebuf/Modules/_hotbuf.c	(original)
+++ python/branches/blais-bytebuf/Modules/_hotbuf.c	Thu May 25 20:25:26 2006
@@ -188,10 +188,14 @@
 static PyObject *
 hotbuf_repr(PyHotbufObject *self)
 {
-    return PyString_FromFormat("<hotbuf ptr %p, size %zd at %p>",
-                               self->b_ptr,
-                               self->b_capacity,
-                               self);
+    return PyString_FromFormat(
+        "<hotbuf mark %zd, position %zd, limit %zd, capacity %zd, ptr %p, at %p>",
+        self->b_mark,
+        self->b_position,
+        self->b_limit, 
+        self->b_capacity,
+        self->b_ptr,
+        self);
 }
 
 /*
@@ -619,12 +623,12 @@
 
     /* Check and extract input string */
     if ( arg == NULL || !PyString_Check(arg) ) {
-        PyErr_SetString(PyExc_ValueError,
+        PyErr_SetString(PyExc_TypeError,
                         "incorrect input type, require string");
         return NULL;
     }
     instring = PyString_AsString(arg);
-    len = strlen(instring);
+    len = PyString_GET_SIZE(arg);
 
     CHECK_LIMIT_ERROR(len);
 

Modified: python/branches/blais-bytebuf/Modules/_struct.c
==============================================================================
--- python/branches/blais-bytebuf/Modules/_struct.c	(original)
+++ python/branches/blais-bytebuf/Modules/_struct.c	Thu May 25 20:25:26 2006
@@ -15,10 +15,10 @@
 typedef int Py_ssize_t;
 #endif
 
-
+/* Forward declarations */
+static Py_ssize_t convertbuffer(PyObject *, void **p);
 
 /* The translation function for each format character is table driven */
-
 typedef struct _formatdef {
 	char format;
 	int size;
@@ -1227,50 +1227,36 @@
 	return s_unpack_internal(soself, buffer + offset);
 }
 
-PyDoc_STRVAR(s_pack__doc__,
-"pack(v1, v2, ...) -> string\n\
-\n\
-Return a string containing values v1, v2, ... packed according to this\n\
-Struct's format. See struct.__doc__ for more on format strings.");
 
-static PyObject *
-s_pack(PyObject *self, PyObject *args)
+/*
+ * Guts of the pack function.
+ *
+ * Takes a struct object, a tuple of arguments, and offset in that tuple of
+ * argument for where to start processing the arguments for packing, and a
+ * character buffer for writing the packed string.  The caller must insure
+ * that the buffer may contain the required length for packing the arguments.
+ * 0 is returned on success, 1 is returned if there is an error.
+ *
+ */
+static int
+s_pack_internal(PyStructObject *soself, PyObject *args, int offset, char* buf)
 {
-	PyStructObject *soself;
-	PyObject *result;
-	char *restart;
 	formatcode *code;
 	Py_ssize_t i;
-	
-	soself = (PyStructObject *)self;
-	assert(PyStruct_Check(self));
-	assert(soself->s_codes != NULL);
-	if (args == NULL || !PyTuple_Check(args) ||
-	    PyTuple_GET_SIZE(args) != soself->s_len)
-	{
-		PyErr_Format(StructError,
-			"pack requires exactly %d arguments", soself->s_len);
-		return NULL;
-	}
-	
-	result = PyString_FromStringAndSize((char *)NULL, soself->s_size);
-	if (result == NULL)
-		return NULL;
-	
-	restart = PyString_AS_STRING(result);
-	memset(restart, '\0', soself->s_size);
-	i = 0;
+
+	memset(buf, '\0', soself->s_size);
+	i = offset;
 	for (code = soself->s_codes; code->fmtdef != NULL; code++) {
 		Py_ssize_t n;
 		PyObject *v;
 		const formatdef *e = code->fmtdef;
-		char *res = restart + code->offset;
+		char *res = buf + code->offset;
 		if (e->format == 's') {
 			v = PyTuple_GET_ITEM(args, i++);
 			if (!PyString_Check(v)) {
 				PyErr_SetString(StructError,
 						"argument for 's' must be a string");
-				goto fail;
+				return -1;
 			}
 			n = PyString_GET_SIZE(v);
 			if (n > code->size)
@@ -1282,7 +1268,7 @@
 			if (!PyString_Check(v)) {
 				PyErr_SetString(StructError,
 						"argument for 'p' must be a string");
-				goto fail;
+				return -1;
 			}
 			n = PyString_GET_SIZE(v);
 			if (n > (code->size - 1))
@@ -1295,16 +1281,141 @@
 		} else {
 			v = PyTuple_GET_ITEM(args, i++);
 			if (e->pack(res, v, e) < 0)
-				goto fail;
+				return -1;
 		}
 	}
 	
+	/* Success */
+	return 0;
+}
+
+
+PyDoc_STRVAR(s_pack__doc__,
+"pack(v1, v2, ...) -> string\n\
+\n\
+Return a string containing values v1, v2, ... packed according to this\n\
+Struct's format. See struct.__doc__ for more on format strings.");
+
+static PyObject *
+s_pack(PyObject *self, PyObject *args)
+{
+	PyStructObject *soself;
+	PyObject *result;
+
+	/* Validate arguments. */
+	soself = (PyStructObject *)self;
+	assert(PyStruct_Check(self));
+	assert(soself->s_codes != NULL);
+	if (args == NULL || !PyTuple_Check(args) ||
+	    PyTuple_GET_SIZE(args) != soself->s_len)
+	{
+		PyErr_Format(StructError,
+			"pack requires exactly %d arguments", soself->s_len);
+		return NULL;
+	}
+	
+	/* Allocate a new string */
+	result = PyString_FromStringAndSize((char *)NULL, soself->s_size);
+	if (result == NULL)
+		return NULL;
+	
+	/* Call the guts */
+	if ( s_pack_internal(soself, args, 0, PyString_AS_STRING(result)) != 0 ) {
+		Py_DECREF(result);
+		return NULL;
+	}
+
 	return result;
+}
 
-fail:
-	Py_DECREF(result);
-	return NULL;
+PyDoc_STRVAR(s_pack_to__doc__,
+"pack_to(buffer, offset, v1, v2, ...)\n\
+\n\
+Pack the values v2, v2, ... according to this Struct's format, write \n\
+the packed bytes into the given buffer at the given offset.  Note that \n\
+the offset is not an optional argument.  See struct.__doc__ for \n\
+more on format strings.");
+
+static PyObject *
+s_pack_to(PyObject *self, PyObject *args)
+{
+	PyStructObject *soself;
+	char *buffer;
+	Py_ssize_t buffer_len, offset;
+
+	/* Validate arguments.  +1 is for the first arg as buffer. */
+	soself = (PyStructObject *)self;
+	assert(PyStruct_Check(self));
+	assert(soself->s_codes != NULL);
+	if (args == NULL || !PyTuple_Check(args) ||
+	    PyTuple_GET_SIZE(args) != (soself->s_len + 2))
+	{
+		PyErr_Format(StructError,
+			     "pack_to requires exactly %d arguments", 
+			     (soself->s_len + 2));
+		return NULL;
+	}
+
+	/* Extract a writable memory buffer from the first argument */
+	buffer_len = convertbuffer(PyTuple_GET_ITEM(args, 0), (void**)&buffer);
+	if (buffer_len < 0) 
+		return NULL;
+
+	/* Extract the offset from the first argument */
+	offset = PyInt_AsLong(PyTuple_GET_ITEM(args, 1));
+
+ 	/* Support negative offsets. */
+	if (offset < 0)
+		offset += buffer_len;
+
+	/* Check boundaries */
+	if (offset < 0 || (buffer_len - offset) < soself->s_size) {
+		PyErr_Format(StructError,
+			     "pack_to requires a buffer of at least %d bytes",
+			     soself->s_size);
+		return NULL;
+	}
 	
+	/* Call the guts */
+	if ( s_pack_internal(soself, args, 2, buffer + offset) != 0 ) {
+		return NULL;
+	}
+
+	return Py_None;
+}
+
+/* 
+ * Important Note: this is a slightly modified copy of
+ * getargs.c:convertbuffer().  All we want to achieve is to convert a PyObject
+ * into a writeable buffer.  We should seriously consider adding this function
+ * to the API somehow.
+ */
+static Py_ssize_t
+convertbuffer(PyObject *arg, void **p)
+{
+	PyBufferProcs *pb = arg->ob_type->tp_as_buffer;
+	Py_ssize_t count;
+	if (pb == NULL ||
+	    pb->bf_getreadbuffer == NULL ||
+	    pb->bf_getsegcount == NULL) {
+
+		PyErr_SetString(StructError,
+				"string or read-only buffer");
+		return -1;
+	}
+
+	if ((*pb->bf_getsegcount)(arg, NULL) != 1) {
+
+		PyErr_SetString(StructError,
+				"string or single-segment read-only buffer");
+		return -1;
+	}
+
+	if ((count = (*pb->bf_getreadbuffer)(arg, 0, p)) < 0) {
+		PyErr_SetString(StructError, "(unspecified)");
+	}
+
+	return count;
 }
 
 
@@ -1312,6 +1423,7 @@
 
 static struct PyMethodDef s_methods[] = {
 	{"pack",	(PyCFunction)s_pack,		METH_VARARGS, s_pack__doc__},
+ 	{"pack_to",	(PyCFunction)s_pack_to,		METH_VARARGS, s_pack_to__doc__}, 
 	{"unpack",	(PyCFunction)s_unpack,		METH_O, s_unpack__doc__},
 	{"unpack_from",	(PyCFunction)s_unpack_from,	METH_KEYWORDS, s_unpack_from__doc__},
 	{NULL,	 NULL}		/* sentinel */


More information about the Python-checkins mailing list