xref: /third_party/python/Objects/accu.c (revision 7db96d56)
1/* Accumulator struct implementation */
2
3#include "Python.h"
4#include "pycore_accu.h"
5
6static PyObject *
7join_list_unicode(PyObject *lst)
8{
9    /* return ''.join(lst) */
10    PyObject *sep, *ret;
11    sep = PyUnicode_FromStringAndSize("", 0);
12    ret = PyUnicode_Join(sep, lst);
13    Py_DECREF(sep);
14    return ret;
15}
16
17int
18_PyAccu_Init(_PyAccu *acc)
19{
20    /* Lazily allocated */
21    acc->large = NULL;
22    acc->small = PyList_New(0);
23    if (acc->small == NULL)
24        return -1;
25    return 0;
26}
27
28static int
29flush_accumulator(_PyAccu *acc)
30{
31    Py_ssize_t nsmall = PyList_GET_SIZE(acc->small);
32    if (nsmall) {
33        int ret;
34        PyObject *joined;
35        if (acc->large == NULL) {
36            acc->large = PyList_New(0);
37            if (acc->large == NULL)
38                return -1;
39        }
40        joined = join_list_unicode(acc->small);
41        if (joined == NULL)
42            return -1;
43        if (PyList_SetSlice(acc->small, 0, nsmall, NULL)) {
44            Py_DECREF(joined);
45            return -1;
46        }
47        ret = PyList_Append(acc->large, joined);
48        Py_DECREF(joined);
49        return ret;
50    }
51    return 0;
52}
53
54int
55_PyAccu_Accumulate(_PyAccu *acc, PyObject *unicode)
56{
57    Py_ssize_t nsmall;
58    assert(PyUnicode_Check(unicode));
59
60    if (PyList_Append(acc->small, unicode))
61        return -1;
62    nsmall = PyList_GET_SIZE(acc->small);
63    /* Each item in a list of unicode objects has an overhead (in 64-bit
64     * builds) of:
65     *   - 8 bytes for the list slot
66     *   - 56 bytes for the header of the unicode object
67     * that is, 64 bytes.  100000 such objects waste more than 6 MiB
68     * compared to a single concatenated string.
69     */
70    if (nsmall < 100000)
71        return 0;
72    return flush_accumulator(acc);
73}
74
75PyObject *
76_PyAccu_FinishAsList(_PyAccu *acc)
77{
78    int ret;
79    PyObject *res;
80
81    ret = flush_accumulator(acc);
82    Py_CLEAR(acc->small);
83    if (ret) {
84        Py_CLEAR(acc->large);
85        return NULL;
86    }
87    res = acc->large;
88    acc->large = NULL;
89    return res;
90}
91
92PyObject *
93_PyAccu_Finish(_PyAccu *acc)
94{
95    PyObject *list, *res;
96    if (acc->large == NULL) {
97        list = acc->small;
98        acc->small = NULL;
99    }
100    else {
101        list = _PyAccu_FinishAsList(acc);
102        if (!list)
103            return NULL;
104    }
105    res = join_list_unicode(list);
106    Py_DECREF(list);
107    return res;
108}
109
110void
111_PyAccu_Destroy(_PyAccu *acc)
112{
113    Py_CLEAR(acc->small);
114    Py_CLEAR(acc->large);
115}
116