@@ -54,6 +54,8 @@ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
5454#include <windows.h>
5555#endif
5656
57+ #include <stddef.h>
58+
5759/* Uncomment to display statistics on interned strings at exit when
5860 using Valgrind or Insecure++. */
5961/* #define INTERNED_STATS 1 */
@@ -1003,6 +1005,31 @@ unicode_fill_invalid(PyObject *unicode, Py_ssize_t old_length)
10031005}
10041006#endif
10051007
1008+ /* Get a bytes object from the UTF-8 cache of the *unicode*.
1009+ * unicode->utf8 must be allocated.
1010+ */
1011+ static PyObject *
1012+ get_utf8_bytes (PyObject * unicode )
1013+ {
1014+ assert (_PyUnicode_HAS_UTF8_MEMORY (unicode ));
1015+ const char * utf8 = _PyUnicode_UTF8 (unicode );
1016+ PyObject * bytes = (PyObject * )(utf8 - offsetof(PyBytesObject , ob_sval ));
1017+ assert (PyBytes_CheckExact (bytes ));
1018+ return bytes ;
1019+ }
1020+
1021+ /* Deallocates UTF-8 cache.
1022+ * unicode->utf8 must be allocated.
1023+ */
1024+ static void
1025+ deallocate_utf8 (PyObject * unicode )
1026+ {
1027+ PyObject * bytes = get_utf8_bytes (unicode );
1028+ Py_DECREF (bytes );
1029+ _PyUnicode_UTF8 (unicode ) = NULL ;
1030+ _PyUnicode_UTF8_LENGTH (unicode ) = 0 ;
1031+ }
1032+
10061033static PyObject *
10071034resize_compact (PyObject * unicode , Py_ssize_t length )
10081035{
@@ -1033,9 +1060,7 @@ resize_compact(PyObject *unicode, Py_ssize_t length)
10331060 new_size = (struct_size + (length + 1 ) * char_size );
10341061
10351062 if (_PyUnicode_HAS_UTF8_MEMORY (unicode )) {
1036- PyObject_DEL (_PyUnicode_UTF8 (unicode ));
1037- _PyUnicode_UTF8 (unicode ) = NULL ;
1038- _PyUnicode_UTF8_LENGTH (unicode ) = 0 ;
1063+ deallocate_utf8 (unicode );
10391064 }
10401065 _Py_DEC_REFTOTAL ;
10411066 _Py_ForgetReference (unicode );
@@ -1099,9 +1124,7 @@ resize_inplace(PyObject *unicode, Py_ssize_t length)
10991124
11001125 if (!share_utf8 && _PyUnicode_HAS_UTF8_MEMORY (unicode ))
11011126 {
1102- PyObject_DEL (_PyUnicode_UTF8 (unicode ));
1103- _PyUnicode_UTF8 (unicode ) = NULL ;
1104- _PyUnicode_UTF8_LENGTH (unicode ) = 0 ;
1127+ deallocate_utf8 (unicode );
11051128 }
11061129
11071130 data = (PyObject * )PyObject_REALLOC (data , new_size );
@@ -1915,8 +1938,9 @@ unicode_dealloc(PyObject *unicode)
19151938
19161939 if (_PyUnicode_HAS_WSTR_MEMORY (unicode ))
19171940 PyObject_DEL (_PyUnicode_WSTR (unicode ));
1918- if (_PyUnicode_HAS_UTF8_MEMORY (unicode ))
1919- PyObject_DEL (_PyUnicode_UTF8 (unicode ));
1941+ if (_PyUnicode_HAS_UTF8_MEMORY (unicode )) {
1942+ deallocate_utf8 (unicode );
1943+ }
19201944 if (!PyUnicode_IS_COMPACT (unicode ) && _PyUnicode_DATA_ANY (unicode ))
19211945 PyObject_DEL (_PyUnicode_DATA_ANY (unicode ));
19221946
@@ -4013,17 +4037,9 @@ PyUnicode_AsUTF8AndSize(PyObject *unicode, Py_ssize_t *psize)
40134037 bytes = _PyUnicode_AsUTF8String (unicode , NULL );
40144038 if (bytes == NULL )
40154039 return NULL ;
4016- _PyUnicode_UTF8 (unicode ) = PyObject_MALLOC (PyBytes_GET_SIZE (bytes ) + 1 );
4017- if (_PyUnicode_UTF8 (unicode ) == NULL ) {
4018- PyErr_NoMemory ();
4019- Py_DECREF (bytes );
4020- return NULL ;
4021- }
4040+ // unicode owns the rerefence of the bytes through the UTF-8 cache.
4041+ _PyUnicode_UTF8 (unicode ) = PyBytes_AS_STRING (bytes );
40224042 _PyUnicode_UTF8_LENGTH (unicode ) = PyBytes_GET_SIZE (bytes );
4023- memcpy (_PyUnicode_UTF8 (unicode ),
4024- PyBytes_AS_STRING (bytes ),
4025- _PyUnicode_UTF8_LENGTH (unicode ) + 1 );
4026- Py_DECREF (bytes );
40274043 }
40284044
40294045 if (psize )
@@ -5398,9 +5414,15 @@ unicode_encode_utf8(PyObject *unicode, _Py_error_handler error_handler,
53985414 if (PyUnicode_READY (unicode ) == -1 )
53995415 return NULL ;
54005416
5401- if (PyUnicode_UTF8 (unicode ))
5417+ if (PyUnicode_UTF8 (unicode )) {
5418+ if (_PyUnicode_HAS_UTF8_MEMORY (unicode )) {
5419+ PyObject * bytes = get_utf8_bytes (unicode );
5420+ Py_INCREF (bytes );
5421+ return bytes ;
5422+ }
54025423 return PyBytes_FromStringAndSize (PyUnicode_UTF8 (unicode ),
54035424 PyUnicode_UTF8_LENGTH (unicode ));
5425+ }
54045426
54055427 kind = PyUnicode_KIND (unicode );
54065428 data = PyUnicode_DATA (unicode );
0 commit comments