Skip to content

Commit 9c11029

Browse files
authored
bpo-37587: json: Use _PyUnicodeWriter when scanning string. (pythonGH-15591)
1 parent a661392 commit 9c11029

2 files changed

Lines changed: 25 additions & 57 deletions

File tree

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
``_json.scanstring`` is now up to 3x faster when there are many backslash
2+
escaped characters in the JSON string.

Modules/_json.c

Lines changed: 23 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -73,19 +73,6 @@ static PyMemberDef encoder_members[] = {
7373
{NULL}
7474
};
7575

76-
static PyObject *
77-
join_list_unicode(PyObject *lst)
78-
{
79-
/* return u''.join(lst) */
80-
static PyObject *sep = NULL;
81-
if (sep == NULL) {
82-
sep = PyUnicode_FromStringAndSize("", 0);
83-
if (sep == NULL)
84-
return NULL;
85-
}
86-
return PyUnicode_Join(sep, lst);
87-
}
88-
8976
/* Forward decls */
9077

9178
static PyObject *
@@ -385,21 +372,6 @@ _build_rval_index_tuple(PyObject *rval, Py_ssize_t idx) {
385372
return tpl;
386373
}
387374

388-
#define APPEND_OLD_CHUNK \
389-
if (chunk != NULL) { \
390-
if (chunks == NULL) { \
391-
chunks = PyList_New(0); \
392-
if (chunks == NULL) { \
393-
goto bail; \
394-
} \
395-
} \
396-
if (PyList_Append(chunks, chunk)) { \
397-
Py_CLEAR(chunk); \
398-
goto bail; \
399-
} \
400-
Py_CLEAR(chunk); \
401-
}
402-
403375
static PyObject *
404376
scanstring_unicode(PyObject *pystr, Py_ssize_t end, int strict, Py_ssize_t *next_end_ptr)
405377
{
@@ -417,12 +389,14 @@ scanstring_unicode(PyObject *pystr, Py_ssize_t end, int strict, Py_ssize_t *next
417389
Py_ssize_t next /* = begin */;
418390
const void *buf;
419391
int kind;
420-
PyObject *chunks = NULL;
421-
PyObject *chunk = NULL;
422392

423393
if (PyUnicode_READY(pystr) == -1)
424394
return 0;
425395

396+
_PyUnicodeWriter writer;
397+
_PyUnicodeWriter_Init(&writer);
398+
writer.overallocate = 1;
399+
426400
len = PyUnicode_GET_LENGTH(pystr);
427401
buf = PyUnicode_DATA(pystr);
428402
kind = PyUnicode_KIND(pystr);
@@ -449,18 +423,26 @@ scanstring_unicode(PyObject *pystr, Py_ssize_t end, int strict, Py_ssize_t *next
449423
}
450424
c = d;
451425
}
452-
if (!(c == '"' || c == '\\')) {
426+
427+
if (c == '"') {
428+
// Fast path for simple case.
429+
if (writer.buffer == NULL) {
430+
PyObject *ret = PyUnicode_Substring(pystr, end, next);
431+
if (ret == NULL) {
432+
goto bail;
433+
}
434+
*next_end_ptr = next + 1;;
435+
return ret;
436+
}
437+
}
438+
else if (c != '\\') {
453439
raise_errmsg("Unterminated string starting at", pystr, begin);
454440
goto bail;
455441
}
442+
456443
/* Pick up this chunk if it's not zero length */
457444
if (next != end) {
458-
APPEND_OLD_CHUNK
459-
chunk = PyUnicode_FromKindAndData(
460-
kind,
461-
(char*)buf + kind * end,
462-
next - end);
463-
if (chunk == NULL) {
445+
if (_PyUnicodeWriter_WriteSubstring(&writer, pystr, end, next) < 0) {
464446
goto bail;
465447
}
466448
}
@@ -551,34 +533,18 @@ scanstring_unicode(PyObject *pystr, Py_ssize_t end, int strict, Py_ssize_t *next
551533
end -= 6;
552534
}
553535
}
554-
APPEND_OLD_CHUNK
555-
chunk = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, &c, 1);
556-
if (chunk == NULL) {
536+
if (_PyUnicodeWriter_WriteChar(&writer, c) < 0) {
557537
goto bail;
558538
}
559539
}
560540

561-
if (chunks == NULL) {
562-
if (chunk != NULL)
563-
rval = chunk;
564-
else
565-
rval = PyUnicode_FromStringAndSize("", 0);
566-
}
567-
else {
568-
APPEND_OLD_CHUNK
569-
rval = join_list_unicode(chunks);
570-
if (rval == NULL) {
571-
goto bail;
572-
}
573-
Py_CLEAR(chunks);
574-
}
575-
541+
rval = _PyUnicodeWriter_Finish(&writer);
576542
*next_end_ptr = end;
577543
return rval;
544+
578545
bail:
579546
*next_end_ptr = -1;
580-
Py_XDECREF(chunks);
581-
Py_XDECREF(chunk);
547+
_PyUnicodeWriter_Dealloc(&writer);
582548
return NULL;
583549
}
584550

0 commit comments

Comments
 (0)