Kaydet (Commit) fff61f2c authored tarafından Serhiy Storchaka's avatar Serhiy Storchaka

Issue #14596: The struct.Struct() objects now use more compact implementation.

üst e67f8e74
...@@ -8,7 +8,6 @@ import sys ...@@ -8,7 +8,6 @@ import sys
from test import support from test import support
ISBIGENDIAN = sys.byteorder == "big" ISBIGENDIAN = sys.byteorder == "big"
IS32BIT = sys.maxsize == 0x7fffffff
integer_codes = 'b', 'B', 'h', 'H', 'i', 'I', 'l', 'L', 'q', 'Q', 'n', 'N' integer_codes = 'b', 'B', 'h', 'H', 'i', 'I', 'l', 'L', 'q', 'Q', 'n', 'N'
byteorders = '', '@', '=', '<', '>', '!' byteorders = '', '@', '=', '<', '>', '!'
...@@ -538,10 +537,6 @@ class StructTest(unittest.TestCase): ...@@ -538,10 +537,6 @@ class StructTest(unittest.TestCase):
hugecount2 = '{}b{}H'.format(sys.maxsize//2, sys.maxsize//2) hugecount2 = '{}b{}H'.format(sys.maxsize//2, sys.maxsize//2)
self.assertRaises(struct.error, struct.calcsize, hugecount2) self.assertRaises(struct.error, struct.calcsize, hugecount2)
if IS32BIT:
def test_crasher(self):
self.assertRaises(MemoryError, struct.pack, "357913941b", "a")
def test_trailing_counter(self): def test_trailing_counter(self):
store = array.array('b', b' '*100) store = array.array('b', b' '*100)
...@@ -578,7 +573,7 @@ class StructTest(unittest.TestCase): ...@@ -578,7 +573,7 @@ class StructTest(unittest.TestCase):
# The size of 'PyStructObject' # The size of 'PyStructObject'
totalsize = support.calcobjsize('2n3P') totalsize = support.calcobjsize('2n3P')
# The size taken up by the 'formatcode' dynamic array # The size taken up by the 'formatcode' dynamic array
totalsize += struct.calcsize('P2n0P') * (number_of_codes + 1) totalsize += struct.calcsize('P3n0P') * (number_of_codes + 1)
support.check_sizeof(self, struct.Struct(format_str), totalsize) support.check_sizeof(self, struct.Struct(format_str), totalsize)
@support.cpython_only @support.cpython_only
...@@ -589,7 +584,7 @@ class StructTest(unittest.TestCase): ...@@ -589,7 +584,7 @@ class StructTest(unittest.TestCase):
self.check_sizeof('B' * 1234, 1234) self.check_sizeof('B' * 1234, 1234)
self.check_sizeof('fd', 2) self.check_sizeof('fd', 2)
self.check_sizeof('xxxxxxxxxxxxxx', 0) self.check_sizeof('xxxxxxxxxxxxxx', 0)
self.check_sizeof('100H', 100) self.check_sizeof('100H', 1)
self.check_sizeof('187s', 1) self.check_sizeof('187s', 1)
self.check_sizeof('20p', 1) self.check_sizeof('20p', 1)
self.check_sizeof('0s', 1) self.check_sizeof('0s', 1)
......
...@@ -91,6 +91,8 @@ Core and Builtins ...@@ -91,6 +91,8 @@ Core and Builtins
Library Library
------- -------
- Issue #14596: The struct.Struct() objects now use more compact implementation.
- Issue #17981: Closed socket on error in SysLogHandler. - Issue #17981: Closed socket on error in SysLogHandler.
- Issue #17964: Fix os.sysconf(): the return type of the C sysconf() function - Issue #17964: Fix os.sysconf(): the return type of the C sysconf() function
......
...@@ -26,6 +26,7 @@ typedef struct _formatcode { ...@@ -26,6 +26,7 @@ typedef struct _formatcode {
const struct _formatdef *fmtdef; const struct _formatdef *fmtdef;
Py_ssize_t offset; Py_ssize_t offset;
Py_ssize_t size; Py_ssize_t size;
Py_ssize_t repeat;
} formatcode; } formatcode;
/* Struct object interface */ /* Struct object interface */
...@@ -1263,7 +1264,7 @@ prepare_s(PyStructObject *self) ...@@ -1263,7 +1264,7 @@ prepare_s(PyStructObject *self)
const char *s; const char *s;
const char *fmt; const char *fmt;
char c; char c;
Py_ssize_t size, len, num, itemsize; Py_ssize_t size, len, ncodes, num, itemsize;
fmt = PyBytes_AS_STRING(self->s_format); fmt = PyBytes_AS_STRING(self->s_format);
...@@ -1272,6 +1273,7 @@ prepare_s(PyStructObject *self) ...@@ -1272,6 +1273,7 @@ prepare_s(PyStructObject *self)
s = fmt; s = fmt;
size = 0; size = 0;
len = 0; len = 0;
ncodes = 0;
while ((c = *s++) != '\0') { while ((c = *s++) != '\0') {
if (Py_ISSPACE(Py_CHARMASK(c))) if (Py_ISSPACE(Py_CHARMASK(c)))
continue; continue;
...@@ -1301,9 +1303,9 @@ prepare_s(PyStructObject *self) ...@@ -1301,9 +1303,9 @@ prepare_s(PyStructObject *self)
switch (c) { switch (c) {
case 's': /* fall through */ case 's': /* fall through */
case 'p': len++; break; case 'p': len++; ncodes++; break;
case 'x': break; case 'x': break;
default: len += num; break; default: len += num; if (num) ncodes++; break;
} }
itemsize = e->size; itemsize = e->size;
...@@ -1318,14 +1320,14 @@ prepare_s(PyStructObject *self) ...@@ -1318,14 +1320,14 @@ prepare_s(PyStructObject *self)
} }
/* check for overflow */ /* check for overflow */
if ((len + 1) > (PY_SSIZE_T_MAX / sizeof(formatcode))) { if ((ncodes + 1) > (PY_SSIZE_T_MAX / sizeof(formatcode))) {
PyErr_NoMemory(); PyErr_NoMemory();
return -1; return -1;
} }
self->s_size = size; self->s_size = size;
self->s_len = len; self->s_len = len;
codes = PyMem_MALLOC((len + 1) * sizeof(formatcode)); codes = PyMem_MALLOC((ncodes + 1) * sizeof(formatcode));
if (codes == NULL) { if (codes == NULL) {
PyErr_NoMemory(); PyErr_NoMemory();
return -1; return -1;
...@@ -1357,23 +1359,24 @@ prepare_s(PyStructObject *self) ...@@ -1357,23 +1359,24 @@ prepare_s(PyStructObject *self)
codes->offset = size; codes->offset = size;
codes->size = num; codes->size = num;
codes->fmtdef = e; codes->fmtdef = e;
codes->repeat = 1;
codes++; codes++;
size += num; size += num;
} else if (c == 'x') { } else if (c == 'x') {
size += num; size += num;
} else { } else if (num) {
while (--num >= 0) { codes->offset = size;
codes->offset = size; codes->size = e->size;
codes->size = e->size; codes->fmtdef = e;
codes->fmtdef = e; codes->repeat = num;
codes++; codes++;
size += e->size; size += e->size * num;
}
} }
} }
codes->fmtdef = NULL; codes->fmtdef = NULL;
codes->offset = size; codes->offset = size;
codes->size = 0; codes->size = 0;
codes->repeat = 0;
return 0; return 0;
...@@ -1462,22 +1465,26 @@ s_unpack_internal(PyStructObject *soself, char *startfrom) { ...@@ -1462,22 +1465,26 @@ s_unpack_internal(PyStructObject *soself, char *startfrom) {
return NULL; return NULL;
for (code = soself->s_codes; code->fmtdef != NULL; code++) { for (code = soself->s_codes; code->fmtdef != NULL; code++) {
PyObject *v;
const formatdef *e = code->fmtdef; const formatdef *e = code->fmtdef;
const char *res = startfrom + code->offset; const char *res = startfrom + code->offset;
if (e->format == 's') { Py_ssize_t j = code->repeat;
v = PyBytes_FromStringAndSize(res, code->size); while (j--) {
} else if (e->format == 'p') { PyObject *v;
Py_ssize_t n = *(unsigned char*)res; if (e->format == 's') {
if (n >= code->size) v = PyBytes_FromStringAndSize(res, code->size);
n = code->size - 1; } else if (e->format == 'p') {
v = PyBytes_FromStringAndSize(res + 1, n); Py_ssize_t n = *(unsigned char*)res;
} else { if (n >= code->size)
v = e->unpack(res, e); n = code->size - 1;
v = PyBytes_FromStringAndSize(res + 1, n);
} else {
v = e->unpack(res, e);
}
if (v == NULL)
goto fail;
PyTuple_SET_ITEM(result, i++, v);
res += code->size;
} }
if (v == NULL)
goto fail;
PyTuple_SET_ITEM(result, i++, v);
} }
return result; return result;
...@@ -1716,62 +1723,67 @@ s_pack_internal(PyStructObject *soself, PyObject *args, int offset, char* buf) ...@@ -1716,62 +1723,67 @@ s_pack_internal(PyStructObject *soself, PyObject *args, int offset, char* buf)
memset(buf, '\0', soself->s_size); memset(buf, '\0', soself->s_size);
i = offset; i = offset;
for (code = soself->s_codes; code->fmtdef != NULL; code++) { for (code = soself->s_codes; code->fmtdef != NULL; code++) {
Py_ssize_t n;
PyObject *v = PyTuple_GET_ITEM(args, i++);
const formatdef *e = code->fmtdef; const formatdef *e = code->fmtdef;
char *res = buf + code->offset; char *res = buf + code->offset;
if (e->format == 's') { Py_ssize_t j = code->repeat;
int isstring; while (j--) {
void *p; PyObject *v = PyTuple_GET_ITEM(args, i++);
isstring = PyBytes_Check(v); if (e->format == 's') {
if (!isstring && !PyByteArray_Check(v)) { Py_ssize_t n;
PyErr_SetString(StructError, int isstring;
"argument for 's' must be a bytes object"); void *p;
return -1; isstring = PyBytes_Check(v);
} if (!isstring && !PyByteArray_Check(v)) {
if (isstring) {
n = PyBytes_GET_SIZE(v);
p = PyBytes_AS_STRING(v);
}
else {
n = PyByteArray_GET_SIZE(v);
p = PyByteArray_AS_STRING(v);
}
if (n > code->size)
n = code->size;
if (n > 0)
memcpy(res, p, n);
} else if (e->format == 'p') {
int isstring;
void *p;
isstring = PyBytes_Check(v);
if (!isstring && !PyByteArray_Check(v)) {
PyErr_SetString(StructError,
"argument for 'p' must be a bytes object");
return -1;
}
if (isstring) {
n = PyBytes_GET_SIZE(v);
p = PyBytes_AS_STRING(v);
}
else {
n = PyByteArray_GET_SIZE(v);
p = PyByteArray_AS_STRING(v);
}
if (n > (code->size - 1))
n = code->size - 1;
if (n > 0)
memcpy(res + 1, p, n);
if (n > 255)
n = 255;
*res = Py_SAFE_DOWNCAST(n, Py_ssize_t, unsigned char);
} else {
if (e->pack(res, v, e) < 0) {
if (PyLong_Check(v) && PyErr_ExceptionMatches(PyExc_OverflowError))
PyErr_SetString(StructError, PyErr_SetString(StructError,
"long too large to convert to int"); "argument for 's' must be a bytes object");
return -1; return -1;
}
if (isstring) {
n = PyBytes_GET_SIZE(v);
p = PyBytes_AS_STRING(v);
}
else {
n = PyByteArray_GET_SIZE(v);
p = PyByteArray_AS_STRING(v);
}
if (n > code->size)
n = code->size;
if (n > 0)
memcpy(res, p, n);
} else if (e->format == 'p') {
Py_ssize_t n;
int isstring;
void *p;
isstring = PyBytes_Check(v);
if (!isstring && !PyByteArray_Check(v)) {
PyErr_SetString(StructError,
"argument for 'p' must be a bytes object");
return -1;
}
if (isstring) {
n = PyBytes_GET_SIZE(v);
p = PyBytes_AS_STRING(v);
}
else {
n = PyByteArray_GET_SIZE(v);
p = PyByteArray_AS_STRING(v);
}
if (n > (code->size - 1))
n = code->size - 1;
if (n > 0)
memcpy(res + 1, p, n);
if (n > 255)
n = 255;
*res = Py_SAFE_DOWNCAST(n, Py_ssize_t, unsigned char);
} else {
if (e->pack(res, v, e) < 0) {
if (PyLong_Check(v) && PyErr_ExceptionMatches(PyExc_OverflowError))
PyErr_SetString(StructError,
"long too large to convert to int");
return -1;
}
} }
res += code->size;
} }
} }
...@@ -1907,8 +1919,11 @@ static PyObject * ...@@ -1907,8 +1919,11 @@ static PyObject *
s_sizeof(PyStructObject *self, void *unused) s_sizeof(PyStructObject *self, void *unused)
{ {
Py_ssize_t size; Py_ssize_t size;
formatcode *code;
size = sizeof(PyStructObject) + sizeof(formatcode) * (self->s_len + 1); size = sizeof(PyStructObject) + sizeof(formatcode);
for (code = self->s_codes; code->fmtdef != NULL; code++)
size += sizeof(formatcode);
return PyLong_FromSsize_t(size); return PyLong_FromSsize_t(size);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment