mirror of https://github.com/python/cpython
refactor unpack, add unpack_from
This commit is contained in:
parent
d5e0dc51cf
commit
eb62127842
|
@ -73,3 +73,15 @@ def unpack(fmt, s):
|
|||
except KeyError:
|
||||
o = _compile(fmt)
|
||||
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)
|
|
@ -437,3 +437,44 @@ def test_705836():
|
|||
TestFailed("expected OverflowError")
|
||||
|
||||
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()
|
||||
|
|
|
@ -32,7 +32,7 @@ typedef struct _formatdef {
|
|||
typedef struct _formatcode {
|
||||
const struct _formatdef *fmtdef;
|
||||
int offset;
|
||||
int repeat;
|
||||
int size;
|
||||
} formatcode;
|
||||
|
||||
/* Struct object interface */
|
||||
|
@ -46,6 +46,7 @@ typedef struct {
|
|||
PyObject *weakreflist; /* List of weak references */
|
||||
} PyStructObject;
|
||||
|
||||
|
||||
#define PyStruct_Check(op) PyObject_TypeCheck(op, &PyStructType)
|
||||
#define PyStruct_CheckExact(op) ((op)->ob_type == &PyStructType)
|
||||
|
||||
|
@ -962,7 +963,7 @@ prepare_s(PyStructObject *self)
|
|||
const char *s;
|
||||
const char *fmt;
|
||||
char c;
|
||||
int size, len, numcodes, num, itemsize, x;
|
||||
int size, len, num, itemsize, x;
|
||||
|
||||
fmt = PyString_AS_STRING(self->s_format);
|
||||
|
||||
|
@ -971,7 +972,6 @@ prepare_s(PyStructObject *self)
|
|||
s = fmt;
|
||||
size = 0;
|
||||
len = 0;
|
||||
numcodes = 0;
|
||||
while ((c = *s++) != '\0') {
|
||||
if (isspace(Py_CHARMASK(c)))
|
||||
continue;
|
||||
|
@ -1003,7 +1003,6 @@ prepare_s(PyStructObject *self)
|
|||
case 'x': break;
|
||||
default: len += num; break;
|
||||
}
|
||||
if (c != 'x') numcodes++;
|
||||
|
||||
itemsize = e->size;
|
||||
size = align(size, c, e);
|
||||
|
@ -1018,7 +1017,7 @@ prepare_s(PyStructObject *self)
|
|||
|
||||
self->s_size = size;
|
||||
self->s_len = len;
|
||||
codes = PyMem_MALLOC((numcodes + 1) * sizeof(formatcode));
|
||||
codes = PyMem_MALLOC((len + 1) * sizeof(formatcode));
|
||||
if (codes == NULL) {
|
||||
PyErr_NoMemory();
|
||||
return -1;
|
||||
|
@ -1043,17 +1042,27 @@ prepare_s(PyStructObject *self)
|
|||
e = getentry(c, f);
|
||||
|
||||
size = align(size, c, e);
|
||||
if (c != 'x') {
|
||||
if (c == 's' || c == 'p') {
|
||||
codes->offset = size;
|
||||
codes->repeat = num;
|
||||
codes->size = num;
|
||||
codes->fmtdef = e;
|
||||
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->offset = -1;
|
||||
codes->repeat = -1;
|
||||
codes->offset = size;
|
||||
codes->size = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1111,6 +1120,46 @@ s_dealloc(PyStructObject *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__,
|
||||
"unpack(str) -> (v1, v2, ...)\n\
|
||||
\n\
|
||||
|
@ -1121,13 +1170,7 @@ strings.");
|
|||
static PyObject *
|
||||
s_unpack(PyObject *self, PyObject *inputstr)
|
||||
{
|
||||
PyStructObject *soself;
|
||||
PyObject *result;
|
||||
char *restart;
|
||||
formatcode *code;
|
||||
Py_ssize_t i;
|
||||
|
||||
soself = (PyStructObject *)self;
|
||||
PyStructObject *soself = (PyStructObject *)self;
|
||||
assert(PyStruct_Check(self));
|
||||
assert(soself->s_codes != NULL);
|
||||
if (inputstr == NULL || !PyString_Check(inputstr) ||
|
||||
|
@ -1136,48 +1179,53 @@ s_unpack(PyObject *self, PyObject *inputstr)
|
|||
"unpack requires a string argument of length %d", soself->s_size);
|
||||
return NULL;
|
||||
}
|
||||
result = PyTuple_New(soself->s_len);
|
||||
if (result == NULL)
|
||||
return s_unpack_internal(soself, PyString_AS_STRING(inputstr));
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
fail:
|
||||
Py_DECREF(result);
|
||||
if (buffer == NULL) {
|
||||
PyErr_Format(StructError,
|
||||
"unpack_from requires a buffer argument");
|
||||
return NULL;
|
||||
};
|
||||
}
|
||||
|
||||
if (offset < 0)
|
||||
offset += buffer_len;
|
||||
|
||||
if (offset < 0 || (buffer_len - offset) < soself->s_size) {
|
||||
PyErr_Format(StructError,
|
||||
"unpack_from requires a buffer of at least %d bytes",
|
||||
soself->s_size);
|
||||
return NULL;
|
||||
}
|
||||
return s_unpack_internal(soself, buffer + offset);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(s_pack__doc__,
|
||||
"pack(v1, v2, ...) -> string\n\
|
||||
|
@ -1225,8 +1273,8 @@ s_pack(PyObject *self, PyObject *args)
|
|||
goto fail;
|
||||
}
|
||||
n = PyString_GET_SIZE(v);
|
||||
if (n > code->repeat)
|
||||
n = code->repeat;
|
||||
if (n > code->size)
|
||||
n = code->size;
|
||||
if (n > 0)
|
||||
memcpy(res, PyString_AS_STRING(v), n);
|
||||
} else if (e->format == 'p') {
|
||||
|
@ -1237,20 +1285,17 @@ s_pack(PyObject *self, PyObject *args)
|
|||
goto fail;
|
||||
}
|
||||
n = PyString_GET_SIZE(v);
|
||||
if (n > (code->repeat - 1))
|
||||
n = code->repeat - 1;
|
||||
if (n > (code->size - 1))
|
||||
n = code->size - 1;
|
||||
if (n > 0)
|
||||
memcpy(res + 1, PyString_AS_STRING(v), n);
|
||||
if (n > 255)
|
||||
n = 255;
|
||||
*res = Py_SAFE_DOWNCAST(n, Py_ssize_t, unsigned char);
|
||||
} else {
|
||||
for (n = 0; n < code->repeat; n++) {
|
||||
v = PyTuple_GET_ITEM(args, i++);
|
||||
if (e->pack(res, v, e) < 0)
|
||||
goto fail;
|
||||
res += e->size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1266,8 +1311,9 @@ fail:
|
|||
/* List of functions */
|
||||
|
||||
static struct PyMethodDef s_methods[] = {
|
||||
{"pack", s_pack, METH_VARARGS, s_pack__doc__},
|
||||
{"unpack", s_unpack, METH_O, s_unpack__doc__},
|
||||
{"pack", (PyCFunction)s_pack, METH_VARARGS, s_pack__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 */
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue