Skip to content

Commit 2d2e23a

Browse files
aivarskiritkatriel
authored andcommitted
pythongh-95385 Fastpath for encoding dict to JSON (pythongh-95374)
1 parent 10dc75f commit 2d2e23a

File tree

2 files changed

+89
-78
lines changed

2 files changed

+89
-78
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Faster ``json.dumps()`` when sorting of keys is not requested (default).

Modules/_json.c

+88-78
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "Python.h"
1313
#include "pycore_ceval.h" // _Py_EnterRecursiveCall()
1414
#include "structmember.h" // PyMemberDef
15+
#include <stdbool.h> // bool
1516

1617

1718
typedef struct _PyScannerObject {
@@ -1491,17 +1492,79 @@ encoder_listencode_obj(PyEncoderObject *s, _PyUnicodeWriter *writer,
14911492
}
14921493
}
14931494

1495+
static int
1496+
encoder_encode_key_value(PyEncoderObject *s, _PyUnicodeWriter *writer, bool *first,
1497+
PyObject *key, PyObject *value, Py_ssize_t indent_level)
1498+
{
1499+
PyObject *keystr = NULL;
1500+
PyObject *encoded;
1501+
1502+
if (PyUnicode_Check(key)) {
1503+
Py_INCREF(key);
1504+
keystr = key;
1505+
}
1506+
else if (PyFloat_Check(key)) {
1507+
keystr = encoder_encode_float(s, key);
1508+
}
1509+
else if (key == Py_True || key == Py_False || key == Py_None) {
1510+
/* This must come before the PyLong_Check because
1511+
True and False are also 1 and 0.*/
1512+
keystr = _encoded_const(key);
1513+
}
1514+
else if (PyLong_Check(key)) {
1515+
keystr = PyLong_Type.tp_repr(key);
1516+
}
1517+
else if (s->skipkeys) {
1518+
return 0;
1519+
}
1520+
else {
1521+
PyErr_Format(PyExc_TypeError,
1522+
"keys must be str, int, float, bool or None, "
1523+
"not %.100s", Py_TYPE(key)->tp_name);
1524+
return -1;
1525+
}
1526+
1527+
if (keystr == NULL) {
1528+
return -1;
1529+
}
1530+
1531+
if (*first) {
1532+
*first = false;
1533+
}
1534+
else {
1535+
if (_PyUnicodeWriter_WriteStr(writer, s->item_separator) < 0) {
1536+
Py_DECREF(keystr);
1537+
return -1;
1538+
}
1539+
}
1540+
1541+
encoded = encoder_encode_string(s, keystr);
1542+
Py_DECREF(keystr);
1543+
if (encoded == NULL) {
1544+
return -1;
1545+
}
1546+
1547+
if (_steal_accumulate(writer, encoded) < 0) {
1548+
return -1;
1549+
}
1550+
if (_PyUnicodeWriter_WriteStr(writer, s->key_separator) < 0) {
1551+
return -1;
1552+
}
1553+
if (encoder_listencode_obj(s, writer, value, indent_level) < 0) {
1554+
return -1;
1555+
}
1556+
return 0;
1557+
}
1558+
14941559
static int
14951560
encoder_listencode_dict(PyEncoderObject *s, _PyUnicodeWriter *writer,
14961561
PyObject *dct, Py_ssize_t indent_level)
14971562
{
14981563
/* Encode Python dict dct a JSON term */
1499-
PyObject *kstr = NULL;
15001564
PyObject *ident = NULL;
1501-
PyObject *it = NULL;
1502-
PyObject *items;
1503-
PyObject *item = NULL;
1504-
Py_ssize_t idx;
1565+
PyObject *items = NULL;
1566+
PyObject *key, *value;
1567+
bool first = true;
15051568

15061569
if (PyDict_GET_SIZE(dct) == 0) /* Fast path */
15071570
return _PyUnicodeWriter_WriteASCIIString(writer, "{}", 2);
@@ -1535,84 +1598,34 @@ encoder_listencode_dict(PyEncoderObject *s, _PyUnicodeWriter *writer,
15351598
*/
15361599
}
15371600

1538-
items = PyMapping_Items(dct);
1539-
if (items == NULL)
1540-
goto bail;
1541-
if (s->sort_keys && PyList_Sort(items) < 0) {
1542-
Py_DECREF(items);
1543-
goto bail;
1544-
}
1545-
it = PyObject_GetIter(items);
1546-
Py_DECREF(items);
1547-
if (it == NULL)
1548-
goto bail;
1549-
idx = 0;
1550-
while ((item = PyIter_Next(it)) != NULL) {
1551-
PyObject *encoded, *key, *value;
1552-
if (!PyTuple_Check(item) || PyTuple_GET_SIZE(item) != 2) {
1553-
PyErr_SetString(PyExc_ValueError, "items must return 2-tuples");
1601+
if (s->sort_keys) {
1602+
1603+
items = PyDict_Items(dct);
1604+
if (items == NULL || PyList_Sort(items) < 0)
15541605
goto bail;
1555-
}
1556-
key = PyTuple_GET_ITEM(item, 0);
1557-
if (PyUnicode_Check(key)) {
1558-
Py_INCREF(key);
1559-
kstr = key;
1560-
}
1561-
else if (PyFloat_Check(key)) {
1562-
kstr = encoder_encode_float(s, key);
1563-
if (kstr == NULL)
1564-
goto bail;
1565-
}
1566-
else if (key == Py_True || key == Py_False || key == Py_None) {
1567-
/* This must come before the PyLong_Check because
1568-
True and False are also 1 and 0.*/
1569-
kstr = _encoded_const(key);
1570-
if (kstr == NULL)
1571-
goto bail;
1572-
}
1573-
else if (PyLong_Check(key)) {
1574-
kstr = PyLong_Type.tp_repr(key);
1575-
if (kstr == NULL) {
1606+
1607+
for (Py_ssize_t i = 0; i < PyList_GET_SIZE(items); i++) {
1608+
PyObject *item = PyList_GET_ITEM(items, i);
1609+
1610+
if (!PyTuple_Check(item) || PyTuple_GET_SIZE(item) != 2) {
1611+
PyErr_SetString(PyExc_ValueError, "items must return 2-tuples");
15761612
goto bail;
15771613
}
1578-
}
1579-
else if (s->skipkeys) {
1580-
Py_DECREF(item);
1581-
continue;
1582-
}
1583-
else {
1584-
PyErr_Format(PyExc_TypeError,
1585-
"keys must be str, int, float, bool or None, "
1586-
"not %.100s", Py_TYPE(key)->tp_name);
1587-
goto bail;
1588-
}
15891614

1590-
if (idx) {
1591-
if (_PyUnicodeWriter_WriteStr(writer, s->item_separator))
1615+
key = PyTuple_GET_ITEM(item, 0);
1616+
value = PyTuple_GET_ITEM(item, 1);
1617+
if (encoder_encode_key_value(s, writer, &first, key, value, indent_level) < 0)
15921618
goto bail;
15931619
}
1620+
Py_CLEAR(items);
15941621

1595-
encoded = encoder_encode_string(s, kstr);
1596-
Py_CLEAR(kstr);
1597-
if (encoded == NULL)
1598-
goto bail;
1599-
if (_PyUnicodeWriter_WriteStr(writer, encoded)) {
1600-
Py_DECREF(encoded);
1601-
goto bail;
1622+
} else {
1623+
Py_ssize_t pos = 0;
1624+
while (PyDict_Next(dct, &pos, &key, &value)) {
1625+
if (encoder_encode_key_value(s, writer, &first, key, value, indent_level) < 0)
1626+
goto bail;
16021627
}
1603-
Py_DECREF(encoded);
1604-
if (_PyUnicodeWriter_WriteStr(writer, s->key_separator))
1605-
goto bail;
1606-
1607-
value = PyTuple_GET_ITEM(item, 1);
1608-
if (encoder_listencode_obj(s, writer, value, indent_level))
1609-
goto bail;
1610-
idx += 1;
1611-
Py_DECREF(item);
16121628
}
1613-
if (PyErr_Occurred())
1614-
goto bail;
1615-
Py_CLEAR(it);
16161629

16171630
if (ident != NULL) {
16181631
if (PyDict_DelItem(s->markers, ident))
@@ -1630,14 +1643,11 @@ encoder_listencode_dict(PyEncoderObject *s, _PyUnicodeWriter *writer,
16301643
return 0;
16311644

16321645
bail:
1633-
Py_XDECREF(it);
1634-
Py_XDECREF(item);
1635-
Py_XDECREF(kstr);
1646+
Py_XDECREF(items);
16361647
Py_XDECREF(ident);
16371648
return -1;
16381649
}
16391650

1640-
16411651
static int
16421652
encoder_listencode_list(PyEncoderObject *s, _PyUnicodeWriter *writer,
16431653
PyObject *seq, Py_ssize_t indent_level)

0 commit comments

Comments
 (0)