| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent cac0167 commit 969af80
5 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -88,6 +88,22 @@ extern void _PyBytes_CheckOverflow( | |||
| 88 | 88 | const char *type_name); | |
| 89 | 89 | #endif | |
| 90 | 90 | ||
| 91 | + | ||
| 92 | + // Return the cached hash value, or -1 if not cached yet. | ||
| 93 | + static inline Py_hash_t | ||
| 94 | + _PyBytes_GET_CACHED_HASH(PyBytesObject *self) | ||
| 95 | + { | ||
| 96 | + _Py_COMP_DIAG_PUSH | ||
| 97 | + _Py_COMP_DIAG_IGNORE_DEPR_DECLS | ||
| 98 | + #ifdef Py_GIL_DISABLED | ||
| 99 | + return _Py_atomic_load_ssize_relaxed(&self->ob_shash); | ||
| 100 | + #else | ||
| 101 | + return self->ob_shash; | ||
| 102 | + #endif | ||
| 103 | + _Py_COMP_DIAG_POP | ||
| 104 | + } | ||
| 105 | + | ||
| 106 | + | ||
| 91 | 107 | /* --- PyBytesWriter ------------------------------------------------------ */ | |
| 92 | 108 | ||
| 93 | 109 | struct PyBytesWriter { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -5,14 +5,15 @@ | |||
| 5 | 5 | """ | |
| 6 | 6 | ||
| 7 | 7 | import array | |
| 8 | + import codecs | ||
| 8 | 9 | import contextlib | |
| 10 | + import copy | ||
| 11 | + import functools | ||
| 9 | 12 | import operator | |
| 10 | 13 | import os | |
| 14 | + import pickle | ||
| 11 | 15 | import re | |
| 12 | 16 | import sys | |
| 13 | - import copy | ||
| 14 | - import functools | ||
| 15 | - import pickle | ||
| 16 | 17 | import tempfile | |
| 17 | 18 | import textwrap | |
| 18 | 19 | import threading | |
@@ -1706,6 +1707,29 @@ def test_take_bytes_optimization(self): | |||
| 1706 | 1707 | bytes_header_size = sys.getsizeof(b'') | |
| 1707 | 1708 | self.assertEqual(ba.__alloc__(), 499 + bytes_header_size) | |
| 1708 | 1709 | ||
| 1710 | + def test_take_bytes_hash(self): | ||
| 1711 | + # gh-158219: bytearray constructor must not use a bytes object | ||
| 1712 | + # if its hash value is already cached. | ||
| 1713 | + | ||
| 1714 | + def encode(string, errors='strict'): | ||
| 1715 | + encoded = string.encode('utf-8') | ||
| 1716 | + hash(encoded) # a codec may hash its own output | ||
| 1717 | + return encoded, len(string) | ||
| 1718 | + | ||
| 1719 | + def hashing_codec(name): | ||
| 1720 | + if name != 'test_take_bytes_hash': | ||
| 1721 | + return None | ||
| 1722 | + return codecs.CodecInfo(encode, None, name=name) | ||
| 1723 | + | ||
| 1724 | + codecs.register(hashing_codec) | ||
| 1725 | + self.addCleanup(codecs.unregister, hashing_codec) | ||
| 1726 | + | ||
| 1727 | + ba = bytearray('hello', 'test_take_bytes_hash') | ||
| 1728 | + ba[0] = ord('H') | ||
| 1729 | + taken = ba.take_bytes() | ||
| 1730 | + self.assertEqual(taken, b'Hello') | ||
| 1731 | + self.assertEqual(hash(taken), hash(b'Hello')) | ||
| 1732 | + | ||
| 1709 | 1733 | def test_take_bytes_reentrant_resize(self): | |
| 1710 | 1734 | # gh-153570: n.__index__() can resize the bytearray, so take_bytes() | |
| 1711 | 1735 | # must re-read the size afterwards. It cached the size before the | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,2 @@ | |||
| 1 | + Fix :class:`bytearray` constructor: do not use a bytes object if its hash | ||
| 2 | + value is already computed. Patch by Cody Maloney and Victor Stinner. | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -58,8 +58,13 @@ bytearray_reinit_from_bytes(PyByteArrayObject *self, Py_ssize_t size) | |||
| 58 | 58 | Py_ssize_t alloc = PyBytes_GET_SIZE(self->ob_bytes_object); | |
| 59 | 59 | assert(0 <= size && size <= alloc); | |
| 60 | 60 | ||
| 61 | - /* Only the empty bytes may be immortal. */ | ||
| 62 | - assert((alloc == 0) == _Py_IsImmortal(self->ob_bytes_object)); | ||
| 61 | + if (alloc != 0) { | ||
| 62 | + assert(_PyBytes_IsMutable(self->ob_bytes_object)); | ||
| 63 | + } | ||
| 64 | + else { | ||
| 65 | + // Use the empty bytes string singleton for an empty bytearray | ||
| 66 | + assert(_Py_IsImmortal(self->ob_bytes_object)); | ||
| 67 | + } | ||
| 63 | 68 | ||
| 64 | 69 | self->ob_bytes = self->ob_start = PyBytes_AS_STRING(self->ob_bytes_object); | |
| 65 | 70 | Py_SET_SIZE(self, size); | |
@@ -1030,7 +1035,8 @@ bytearray___init___impl(PyByteArrayObject *self, PyObject *arg, | |||
| 1030 | 1035 | ||
| 1031 | 1036 | /* Most encodes return a new unique bytes, just use it as buffer. */ | |
| 1032 | 1037 | if (_PyObject_IsUniquelyReferenced(encoded) | |
| 1033 | - && PyBytes_CheckExact(encoded)) | ||
| 1038 | + && PyBytes_CheckExact(encoded) | ||
| 1039 | + && _PyBytes_GET_CACHED_HASH((PyBytesObject*)encoded) == -1) | ||
| 1034 | 1040 | { | |
| 1035 | 1041 | Py_ssize_t size = PyBytes_GET_SIZE(encoded); | |
| 1036 | 1042 | self->ob_bytes_object = encoded; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -63,18 +63,7 @@ _Py_COMP_DIAG_IGNORE_DEPR_DECLS | |||
| 63 | 63 | _Py_COMP_DIAG_POP | |
| 64 | 64 | } | |
| 65 | 65 | ||
| 66 | - static inline Py_hash_t | ||
| 67 | - get_ob_shash(PyBytesObject *a) | ||
| 68 | - { | ||
| 69 | - _Py_COMP_DIAG_PUSH | ||
| 70 | - _Py_COMP_DIAG_IGNORE_DEPR_DECLS | ||
| 71 | - #ifdef Py_GIL_DISABLED | ||
| 72 | - return _Py_atomic_load_ssize_relaxed(&a->ob_shash); | ||
| 73 | - #else | ||
| 74 | - return a->ob_shash; | ||
| 75 | - #endif | ||
| 76 | - _Py_COMP_DIAG_POP | ||
| 77 | - } | ||
| 66 | + #define get_ob_shash(op) _PyBytes_GET_CACHED_HASH(op) | ||
| 78 | 67 | ||
| 79 | 68 | ||
| 80 | 69 | /* | |
@@ -3346,6 +3335,12 @@ _PyBytes_IsMutable(PyObject *self) | |||
| 3346 | 3335 | unsigned char ch = PyBytes_AS_STRING(self)[0]; | |
| 3347 | 3336 | assert(self != (PyObject*)CHARACTER(ch)); | |
| 3348 | 3337 | } | |
| 3338 | + | ||
| 3339 | + // gh-158219: The hash value must not be cached yet. Otherwise, it means | ||
| 3340 | + // that the bytes object was already used in Python somehow (ex: as a | ||
| 3341 | + // dictionary key). | ||
| 3342 | + assert(get_ob_shash((PyBytesObject *)self) == -1); | ||
| 3343 | + | ||
| 3349 | 3344 | return 1; | |
| 3350 | 3345 | } | |
| 3351 | 3346 | #endif | |
| Back | FazBrowse Home | New Git URL |
0 commit comments