Commit 3daaafb7 authored by Serhiy Storchaka's avatar Serhiy Storchaka Committed by GitHub

bpo-32037: Use the INT opcode for 32-bit integers in protocol 0 pickles. (#4407)

parent 0a2abdfc
...@@ -674,6 +674,9 @@ class _Pickler: ...@@ -674,6 +674,9 @@ class _Pickler:
else: else:
self.write(LONG4 + pack("<i", n) + encoded) self.write(LONG4 + pack("<i", n) + encoded)
return return
if -0x80000000 <= obj <= 0x7fffffff:
self.write(INT + repr(obj).encode("ascii") + b'\n')
else:
self.write(LONG + repr(obj).encode("ascii") + b'L\n') self.write(LONG + repr(obj).encode("ascii") + b'L\n')
dispatch[int] = save_long dispatch[int] = save_long
......
...@@ -2480,35 +2480,35 @@ _dis_test = r""" ...@@ -2480,35 +2480,35 @@ _dis_test = r"""
0: ( MARK 0: ( MARK
1: l LIST (MARK at 0) 1: l LIST (MARK at 0)
2: p PUT 0 2: p PUT 0
5: L LONG 1 5: I INT 1
9: a APPEND 8: a APPEND
10: L LONG 2 9: I INT 2
14: a APPEND 12: a APPEND
15: ( MARK 13: ( MARK
16: L LONG 3 14: I INT 3
20: L LONG 4 17: I INT 4
24: t TUPLE (MARK at 15) 20: t TUPLE (MARK at 13)
25: p PUT 1 21: p PUT 1
28: a APPEND 24: a APPEND
29: ( MARK 25: ( MARK
30: d DICT (MARK at 29) 26: d DICT (MARK at 25)
31: p PUT 2 27: p PUT 2
34: c GLOBAL '_codecs encode' 30: c GLOBAL '_codecs encode'
50: p PUT 3 46: p PUT 3
53: ( MARK 49: ( MARK
54: V UNICODE 'abc' 50: V UNICODE 'abc'
59: p PUT 4 55: p PUT 4
62: V UNICODE 'latin1' 58: V UNICODE 'latin1'
70: p PUT 5 66: p PUT 5
73: t TUPLE (MARK at 53) 69: t TUPLE (MARK at 49)
74: p PUT 6 70: p PUT 6
77: R REDUCE 73: R REDUCE
78: p PUT 7 74: p PUT 7
81: V UNICODE 'def' 77: V UNICODE 'def'
86: p PUT 8 82: p PUT 8
89: s SETITEM 85: s SETITEM
90: a APPEND 86: a APPEND
91: . STOP 87: . STOP
highest protocol among opcodes = 0 highest protocol among opcodes = 0
Try again with a "binary" pickle. Try again with a "binary" pickle.
...@@ -2577,13 +2577,13 @@ highest protocol among opcodes = 0 ...@@ -2577,13 +2577,13 @@ highest protocol among opcodes = 0
93: p PUT 6 93: p PUT 6
96: V UNICODE 'value' 96: V UNICODE 'value'
103: p PUT 7 103: p PUT 7
106: L LONG 42 106: I INT 42
111: s SETITEM 110: s SETITEM
112: b BUILD 111: b BUILD
113: a APPEND 112: a APPEND
114: g GET 5 113: g GET 5
117: a APPEND 116: a APPEND
118: . STOP 117: . STOP
highest protocol among opcodes = 0 highest protocol among opcodes = 0
>>> dis(pickle.dumps(x, 1)) >>> dis(pickle.dumps(x, 1))
......
...@@ -1821,7 +1821,7 @@ class AbstractPickleTests(unittest.TestCase): ...@@ -1821,7 +1821,7 @@ class AbstractPickleTests(unittest.TestCase):
with self.subTest(proto=proto): with self.subTest(proto=proto):
s = self.dumps(x, proto) s = self.dumps(x, proto)
if proto < 1: if proto < 1:
self.assertIn(b'\nL64206', s) # LONG self.assertIn(b'\nI64206', s) # INT
else: else:
self.assertIn(b'M\xce\xfa', s) # BININT2 self.assertIn(b'M\xce\xfa', s) # BININT2
self.assertEqual(opcode_in_pickle(pickle.NEWOBJ, s), self.assertEqual(opcode_in_pickle(pickle.NEWOBJ, s),
...@@ -1837,7 +1837,7 @@ class AbstractPickleTests(unittest.TestCase): ...@@ -1837,7 +1837,7 @@ class AbstractPickleTests(unittest.TestCase):
with self.subTest(proto=proto): with self.subTest(proto=proto):
s = self.dumps(x, proto) s = self.dumps(x, proto)
if proto < 1: if proto < 1:
self.assertIn(b'\nL64206', s) # LONG self.assertIn(b'\nI64206', s) # INT
elif proto < 2: elif proto < 2:
self.assertIn(b'M\xce\xfa', s) # BININT2 self.assertIn(b'M\xce\xfa', s) # BININT2
elif proto < 4: elif proto < 4:
...@@ -1857,7 +1857,7 @@ class AbstractPickleTests(unittest.TestCase): ...@@ -1857,7 +1857,7 @@ class AbstractPickleTests(unittest.TestCase):
with self.subTest(proto=proto): with self.subTest(proto=proto):
s = self.dumps(x, proto) s = self.dumps(x, proto)
if proto < 1: if proto < 1:
self.assertIn(b'\nL64206', s) # LONG self.assertIn(b'\nI64206', s) # INT
elif proto < 2: elif proto < 2:
self.assertIn(b'M\xce\xfa', s) # BININT2 self.assertIn(b'M\xce\xfa', s) # BININT2
elif proto < 4: elif proto < 4:
......
Integers that fit in a signed 32-bit integer will be now pickled with
protocol 0 using the INT opcode. This will decrease the size of a pickle,
speed up pickling and unpickling, and make these integers be unpickled as
int instances in Python 2.
...@@ -1858,18 +1858,13 @@ save_long(PicklerObject *self, PyObject *obj) ...@@ -1858,18 +1858,13 @@ save_long(PicklerObject *self, PyObject *obj)
PyObject *repr = NULL; PyObject *repr = NULL;
Py_ssize_t size; Py_ssize_t size;
long val; long val;
int overflow;
int status = 0; int status = 0;
const char long_op = LONG; val= PyLong_AsLongAndOverflow(obj, &overflow);
if (!overflow && (sizeof(long) <= 4 ||
val= PyLong_AsLong(obj); (val <= 0x7fffffffL && val >= (-0x7fffffffL - 1))))
if (val == -1 && PyErr_Occurred()) { {
/* out of range for int pickling */
PyErr_Clear();
}
else if (self->bin &&
(sizeof(long) <= 4 ||
(val <= 0x7fffffffL && val >= (-0x7fffffffL - 1)))) {
/* result fits in a signed 4-byte integer. /* result fits in a signed 4-byte integer.
Note: we can't use -0x80000000L in the above condition because some Note: we can't use -0x80000000L in the above condition because some
...@@ -1882,31 +1877,35 @@ save_long(PicklerObject *self, PyObject *obj) ...@@ -1882,31 +1877,35 @@ save_long(PicklerObject *self, PyObject *obj)
char pdata[32]; char pdata[32];
Py_ssize_t len = 0; Py_ssize_t len = 0;
if (self->bin) {
pdata[1] = (unsigned char)(val & 0xff); pdata[1] = (unsigned char)(val & 0xff);
pdata[2] = (unsigned char)((val >> 8) & 0xff); pdata[2] = (unsigned char)((val >> 8) & 0xff);
pdata[3] = (unsigned char)((val >> 16) & 0xff); pdata[3] = (unsigned char)((val >> 16) & 0xff);
pdata[4] = (unsigned char)((val >> 24) & 0xff); pdata[4] = (unsigned char)((val >> 24) & 0xff);
if ((pdata[4] == 0) && (pdata[3] == 0)) { if ((pdata[4] != 0) || (pdata[3] != 0)) {
if (pdata[2] == 0) { pdata[0] = BININT;
pdata[0] = BININT1; len = 5;
len = 2;
} }
else { else if (pdata[2] != 0) {
pdata[0] = BININT2; pdata[0] = BININT2;
len = 3; len = 3;
} }
else {
pdata[0] = BININT1;
len = 2;
}
} }
else { else {
pdata[0] = BININT; sprintf(pdata, "%c%ld\n", INT, val);
len = 5; len = strlen(pdata);
} }
if (_Pickler_Write(self, pdata, len) < 0) if (_Pickler_Write(self, pdata, len) < 0)
return -1; return -1;
return 0; return 0;
} }
assert(!PyErr_Occurred());
if (self->proto >= 2) { if (self->proto >= 2) {
/* Linear-time pickling. */ /* Linear-time pickling. */
...@@ -1986,6 +1985,7 @@ save_long(PicklerObject *self, PyObject *obj) ...@@ -1986,6 +1985,7 @@ save_long(PicklerObject *self, PyObject *obj)
goto error; goto error;
} }
else { else {
const char long_op = LONG;
const char *string; const char *string;
/* proto < 2: write the repr and newline. This is quadratic-time (in /* proto < 2: write the repr and newline. This is quadratic-time (in
......
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