refactor unpack, add unpack_from

This commit is contained in:
Bob Ippolito 2006-05-24 15:32:06 +00:00
parent d5e0dc51cf
commit eb62127842
3 changed files with 168 additions and 69 deletions

View File

@ -73,3 +73,15 @@ def unpack(fmt, s):
except KeyError: except KeyError:
o = _compile(fmt) o = _compile(fmt)
return o.unpack(s) return o.unpack(s)
def unpack_from(fmt, buf, offset=0):
"""
Unpack the buffer, containing packed C structure data, according to
fmt starting at offset. Requires len(buffer[offset:]) >= calcsize(fmt).
See struct.__doc__ for more on format strings.
"""
try:
o = _cache[fmt]
except KeyError:
o = _compile(fmt)
return o.unpack_from(buf, offset)

View File

@ -437,3 +437,44 @@ def test_705836():
TestFailed("expected OverflowError") TestFailed("expected OverflowError")
test_705836() 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):
for fmt in ('B', 'H', 'I', 'L'):
any_err(struct.pack, endian + fmt, cls(-1))
any_err(struct.pack, endian + 'B', cls(300))
any_err(struct.pack, endian + 'H', cls(70000))
any_err(struct.pack, endian + 'I', sys.maxint * 4L)
any_err(struct.pack, endian + 'L', sys.maxint * 4L)
if 0:
# TODO: bug #1229380
test_1229380()

View File

@ -32,7 +32,7 @@ typedef struct _formatdef {
typedef struct _formatcode { typedef struct _formatcode {
const struct _formatdef *fmtdef; const struct _formatdef *fmtdef;
int offset; int offset;
int repeat; int size;
} formatcode; } formatcode;
/* Struct object interface */ /* Struct object interface */
@ -46,6 +46,7 @@ typedef struct {
PyObject *weakreflist; /* List of weak references */ PyObject *weakreflist; /* List of weak references */
} PyStructObject; } PyStructObject;
#define PyStruct_Check(op) PyObject_TypeCheck(op, &PyStructType) #define PyStruct_Check(op) PyObject_TypeCheck(op, &PyStructType)
#define PyStruct_CheckExact(op) ((op)->ob_type == &PyStructType) #define PyStruct_CheckExact(op) ((op)->ob_type == &PyStructType)
@ -962,7 +963,7 @@ prepare_s(PyStructObject *self)
const char *s; const char *s;
const char *fmt; const char *fmt;
char c; char c;
int size, len, numcodes, num, itemsize, x; int size, len, num, itemsize, x;
fmt = PyString_AS_STRING(self->s_format); fmt = PyString_AS_STRING(self->s_format);
@ -971,7 +972,6 @@ prepare_s(PyStructObject *self)
s = fmt; s = fmt;
size = 0; size = 0;
len = 0; len = 0;
numcodes = 0;
while ((c = *s++) != '\0') { while ((c = *s++) != '\0') {
if (isspace(Py_CHARMASK(c))) if (isspace(Py_CHARMASK(c)))
continue; continue;
@ -1003,7 +1003,6 @@ prepare_s(PyStructObject *self)
case 'x': break; case 'x': break;
default: len += num; break; default: len += num; break;
} }
if (c != 'x') numcodes++;
itemsize = e->size; itemsize = e->size;
size = align(size, c, e); size = align(size, c, e);
@ -1018,7 +1017,7 @@ prepare_s(PyStructObject *self)
self->s_size = size; self->s_size = size;
self->s_len = len; self->s_len = len;
codes = PyMem_MALLOC((numcodes + 1) * sizeof(formatcode)); codes = PyMem_MALLOC((len + 1) * sizeof(formatcode));
if (codes == NULL) { if (codes == NULL) {
PyErr_NoMemory(); PyErr_NoMemory();
return -1; return -1;
@ -1043,17 +1042,27 @@ prepare_s(PyStructObject *self)
e = getentry(c, f); e = getentry(c, f);
size = align(size, c, e); size = align(size, c, e);
if (c != 'x') { if (c == 's' || c == 'p') {
codes->offset = size; codes->offset = size;
codes->repeat = num; codes->size = num;
codes->fmtdef = e; codes->fmtdef = e;
codes++; codes++;
size += num;
} else if (c == 'x') {
size += num;
} else {
while (--num >= 0) {
codes->offset = size;
codes->size = e->size;
codes->fmtdef = e;
codes++;
size += e->size;
}
} }
size += num * e->size;
} }
codes->fmtdef = NULL; codes->fmtdef = NULL;
codes->offset = -1; codes->offset = size;
codes->repeat = -1; codes->size = 0;
return 0; return 0;
} }
@ -1111,6 +1120,46 @@ s_dealloc(PyStructObject *s)
s->ob_type->tp_free((PyObject *)s); s->ob_type->tp_free((PyObject *)s);
} }
static PyObject *
s_unpack_internal(PyStructObject *soself, char *startfrom) {
formatcode *code;
Py_ssize_t i = 0;
PyObject *result = PyTuple_New(soself->s_len);
if (result == NULL)
return NULL;
for (code = soself->s_codes; code->fmtdef != NULL; code++) {
PyObject *v;
const formatdef *e = code->fmtdef;
const char *res = startfrom + code->offset;
if (e->format == 's') {
v = PyString_FromStringAndSize(res, code->size);
if (v == NULL)
goto fail;
PyTuple_SET_ITEM(result, i++, v);
} else if (e->format == 'p') {
Py_ssize_t n = *(unsigned char*)res;
if (n >= code->size)
n = code->size - 1;
v = PyString_FromStringAndSize(res + 1, n);
if (v == NULL)
goto fail;
PyTuple_SET_ITEM(result, i++, v);
} else {
v = e->unpack(res, e);
if (v == NULL)
goto fail;
PyTuple_SET_ITEM(result, i++, v);
}
}
return result;
fail:
Py_DECREF(result);
return NULL;
};
PyDoc_STRVAR(s_unpack__doc__, PyDoc_STRVAR(s_unpack__doc__,
"unpack(str) -> (v1, v2, ...)\n\ "unpack(str) -> (v1, v2, ...)\n\
\n\ \n\
@ -1121,63 +1170,62 @@ strings.");
static PyObject * static PyObject *
s_unpack(PyObject *self, PyObject *inputstr) s_unpack(PyObject *self, PyObject *inputstr)
{ {
PyStructObject *soself; PyStructObject *soself = (PyStructObject *)self;
PyObject *result;
char *restart;
formatcode *code;
Py_ssize_t i;
soself = (PyStructObject *)self;
assert(PyStruct_Check(self)); assert(PyStruct_Check(self));
assert(soself->s_codes != NULL); assert(soself->s_codes != NULL);
if (inputstr == NULL || !PyString_Check(inputstr) || if (inputstr == NULL || !PyString_Check(inputstr) ||
PyString_GET_SIZE(inputstr) != soself->s_size) { PyString_GET_SIZE(inputstr) != soself->s_size) {
PyErr_Format(StructError, PyErr_Format(StructError,
"unpack requires a string argument of length %d", soself->s_size); "unpack requires a string argument of length %d", soself->s_size);
return NULL; return NULL;
} }
result = PyTuple_New(soself->s_len); return s_unpack_internal(soself, PyString_AS_STRING(inputstr));
if (result == NULL) }
PyDoc_STRVAR(s_unpack_from__doc__,
"unpack_from(buffer[, offset]) -> (v1, v2, ...)\n\
\n\
Return tuple containing values unpacked according to this Struct's format.\n\
Unlike unpack, unpack_from can unpack values from any object supporting\n\
the buffer API, not just str. Requires len(buffer[offset:]) >= self.size.\n\
See struct.__doc__ for more on format strings.");
static PyObject *
s_unpack_from(PyObject *self, PyObject *args, PyObject *kwds)
{
static char *kwlist[] = {"buffer", "offset", 0};
#if (PY_VERSION_HEX < 0x02050000)
static char *fmt = "z#|i:unpack_from";
#else
static char *fmt = "z#|n:unpack_from";
#endif
Py_ssize_t buffer_len = 0, offset = 0;
char *buffer = NULL;
PyStructObject *soself = (PyStructObject *)self;
assert(PyStruct_Check(self));
assert(soself->s_codes != NULL);
if (!PyArg_ParseTupleAndKeywords(args, kwds, fmt, kwlist,
&buffer, &buffer_len, &offset))
return NULL;
if (buffer == NULL) {
PyErr_Format(StructError,
"unpack_from requires a buffer argument");
return NULL; return NULL;
restart = PyString_AS_STRING(inputstr);
i = 0;
for (code = soself->s_codes; code->fmtdef != NULL; code++) {
Py_ssize_t n;
PyObject *v;
const formatdef *e = code->fmtdef;
const char *res = restart + code->offset;
if (e->format == 's') {
v = PyString_FromStringAndSize(res, code->repeat);
if (v == NULL)
goto fail;
PyTuple_SET_ITEM(result, i++, v);
} else if (e->format == 'p') {
n = *(unsigned char*)res;
if (n >= code->repeat)
n = code->repeat - 1;
v = PyString_FromStringAndSize(res + 1, n);
if (v == NULL)
goto fail;
PyTuple_SET_ITEM(result, i++, v);
} else {
for (n = 0; n < code->repeat; n++) {
v = e->unpack(res, e);
if (v == NULL)
goto fail;
PyTuple_SET_ITEM(result, i++, v);
res += e->size;
}
}
} }
if (offset < 0)
offset += buffer_len;
return result; if (offset < 0 || (buffer_len - offset) < soself->s_size) {
fail: PyErr_Format(StructError,
Py_DECREF(result); "unpack_from requires a buffer of at least %d bytes",
return NULL; soself->s_size);
}; return NULL;
}
return s_unpack_internal(soself, buffer + offset);
}
PyDoc_STRVAR(s_pack__doc__, PyDoc_STRVAR(s_pack__doc__,
"pack(v1, v2, ...) -> string\n\ "pack(v1, v2, ...) -> string\n\
@ -1225,8 +1273,8 @@ s_pack(PyObject *self, PyObject *args)
goto fail; goto fail;
} }
n = PyString_GET_SIZE(v); n = PyString_GET_SIZE(v);
if (n > code->repeat) if (n > code->size)
n = code->repeat; n = code->size;
if (n > 0) if (n > 0)
memcpy(res, PyString_AS_STRING(v), n); memcpy(res, PyString_AS_STRING(v), n);
} else if (e->format == 'p') { } else if (e->format == 'p') {
@ -1237,20 +1285,17 @@ s_pack(PyObject *self, PyObject *args)
goto fail; goto fail;
} }
n = PyString_GET_SIZE(v); n = PyString_GET_SIZE(v);
if (n > (code->repeat - 1)) if (n > (code->size - 1))
n = code->repeat - 1; n = code->size - 1;
if (n > 0) if (n > 0)
memcpy(res + 1, PyString_AS_STRING(v), n); memcpy(res + 1, PyString_AS_STRING(v), n);
if (n > 255) if (n > 255)
n = 255; n = 255;
*res = Py_SAFE_DOWNCAST(n, Py_ssize_t, unsigned char); *res = Py_SAFE_DOWNCAST(n, Py_ssize_t, unsigned char);
} else { } else {
for (n = 0; n < code->repeat; n++) { v = PyTuple_GET_ITEM(args, i++);
v = PyTuple_GET_ITEM(args, i++); if (e->pack(res, v, e) < 0)
if (e->pack(res, v, e) < 0) goto fail;
goto fail;
res += e->size;
}
} }
} }
@ -1266,8 +1311,9 @@ fail:
/* List of functions */ /* List of functions */
static struct PyMethodDef s_methods[] = { static struct PyMethodDef s_methods[] = {
{"pack", s_pack, METH_VARARGS, s_pack__doc__}, {"pack", (PyCFunction)s_pack, METH_VARARGS, s_pack__doc__},
{"unpack", s_unpack, METH_O, s_unpack__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 */ {NULL, NULL} /* sentinel */
}; };