[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