Skip to content

Commit 0a01abf

Browse files
committed
bpo-39087: Make PyUnicode_AsUTF8AndSize faster.
1 parent 1f44e77 commit 0a01abf

1 file changed

Lines changed: 41 additions & 19 deletions

File tree

Objects/unicodeobject.c

Lines changed: 41 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
10061033
static PyObject*
10071034
resize_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

Comments
 (0)