@@ -39,8 +39,8 @@ typedef struct {
3939#define bytesiobuf_CAST (op ) ((bytesiobuf *)(op))
4040
4141/* The bytesio object can be in three states:
42- * Py_REFCNT(buf) == 1, exports == 0.
43- * Py_REFCNT(buf) > 1. exports == 0,
42+ * Py_REFCNT(buf) == 1, the hash of buf is not cached, exports == 0.
43+ * Py_REFCNT(buf) > 1 or the hash of buf is cached . exports == 0,
4444 first modification or export causes the internal buffer copying.
4545 * exports > 0. Any modifications are forbidden. Every exported buffer
4646 keeps a reference to buf, so it outlives closing of the bytesio object.
@@ -77,10 +77,19 @@ check_exports(bytesio *self)
7777 return NULL; \
7878 }
7979
80+ /* If the hash value is cached, the internal bytes object was already used in
81+ Python somehow (ex: as a dictionary key). Modifying it in place would leave
82+ the hash of the old contents, even if nothing else references it anymore. */
83+ #define HASHED_BUF (self ) \
84+ (_PyBytes_GET_CACHED_HASH((PyBytesObject *)(self)->buf) != -1)
85+
8086#ifdef Py_GIL_DISABLED
81- #define SHARED_BUF (self ) ((self)->buf_shared || !_PyObject_IsUniquelyReferenced((self)->buf))
87+ #define SHARED_BUF (self ) \
88+ ((self)->buf_shared || !_PyObject_IsUniquelyReferenced((self)->buf) \
89+ || HASHED_BUF(self))
8290#else
83- #define SHARED_BUF (self ) (!_PyObject_IsUniquelyReferenced((self)->buf))
91+ #define SHARED_BUF (self ) \
92+ (!_PyObject_IsUniquelyReferenced((self)->buf) || HASHED_BUF(self))
8493#endif
8594
8695static inline void
@@ -269,6 +278,7 @@ write_bytes_lock_held(bytesio *self, PyObject *b)
269278 goto done ;
270279 }
271280 }
281+ assert (_PyBytes_IsMutable (self -> buf ));
272282
273283 if (self -> pos > self -> string_size ) {
274284 /* In case of overseek, pad with null bytes the buffer region between
0 commit comments