1 /* Accumulator struct implementation */
2 
3 #include "Python.h"
4 #include "pycore_accu.h"
5 
6 static PyObject *
join_list_unicode(PyObject * lst)7 join_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 
17 int
_PyAccu_Init(_PyAccu * acc)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 
28 static int
flush_accumulator(_PyAccu * acc)29 flush_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 
54 int
_PyAccu_Accumulate(_PyAccu * acc,PyObject * unicode)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 
75 PyObject *
_PyAccu_FinishAsList(_PyAccu * acc)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 
92 PyObject *
_PyAccu_Finish(_PyAccu * acc)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 
110 void
_PyAccu_Destroy(_PyAccu * acc)111 _PyAccu_Destroy(_PyAccu *acc)
112 {
113     Py_CLEAR(acc->small);
114     Py_CLEAR(acc->large);
115 }
116