Skip to content

Commit e32789e

Browse files
committed
gh-129711: Add a no-escape fast path to the _json str escapers
ascii_escape_unicode() and escape_unicode() now return the input bulk-copied with surrounding quotes when nothing needs escaping, via a shared quote_unescaped_unicode() helper, like the writer-based write_escaped_ascii()/write_escaped_unicode() already do in-place. This speeds up json.dumps() of clean strings by 1.2x-2.2x. No behavior change.
1 parent 3f99ebe commit e32789e

2 files changed

Lines changed: 31 additions & 0 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Speed up :func:`json.dump` for strings that need no escaping.

Modules/_json.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,28 @@ ascii_escape_unicode_and_size(const void *input, int kind, Py_ssize_t input_char
223223
return rval;
224224
}
225225

226+
static PyObject *
227+
quote_unescaped_unicode(PyObject *pystr)
228+
{
229+
Py_ssize_t len = PyUnicode_GET_LENGTH(pystr);
230+
PyObject *rval = PyUnicode_New(len + 2, PyUnicode_MAX_CHAR_VALUE(pystr));
231+
if (rval == NULL) {
232+
return NULL;
233+
}
234+
int kind = PyUnicode_KIND(rval);
235+
void *data = PyUnicode_DATA(rval);
236+
PyUnicode_WRITE(kind, data, 0, '"');
237+
if (PyUnicode_CopyCharacters(rval, 1, pystr, 0, len) < 0) {
238+
Py_DECREF(rval);
239+
return NULL;
240+
}
241+
PyUnicode_WRITE(kind, data, len + 1, '"');
242+
#ifdef Py_DEBUG
243+
assert(_PyUnicode_CheckConsistency(rval, 1));
244+
#endif
245+
return rval;
246+
}
247+
226248
static PyObject *
227249
ascii_escape_unicode(PyObject *pystr)
228250
{
@@ -236,6 +258,10 @@ ascii_escape_unicode(PyObject *pystr)
236258
return NULL;
237259
}
238260

261+
if (output_size == input_chars + 2) {
262+
return quote_unescaped_unicode(pystr);
263+
}
264+
239265
return ascii_escape_unicode_and_size(input, kind, input_chars, output_size);
240266
}
241267

@@ -383,6 +409,10 @@ escape_unicode(PyObject *pystr)
383409
return NULL;
384410
}
385411

412+
if (output_size == input_chars + 2) {
413+
return quote_unescaped_unicode(pystr);
414+
}
415+
386416
return escape_unicode_and_size(input, kind, maxchar, input_chars, output_size);
387417
}
388418

0 commit comments

Comments
 (0)