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