/home/mdboom/Work/builds/cpython/Objects/stringlib/join.h
Line | Count | Source (jump to first uncovered line) |
1 | /* stringlib: bytes joining implementation */ |
2 | |
3 | #if STRINGLIB_IS_UNICODE |
4 | #error join.h only compatible with byte-wise strings |
5 | #endif |
6 | |
7 | Py_LOCAL_INLINE(PyObject *) |
8 | STRINGLIB(bytes_join)(PyObject *sep, PyObject *iterable) |
9 | { |
10 | const char *sepstr = STRINGLIB_STR(sep); |
11 | Py_ssize_t seplen = STRINGLIB_LEN(sep); |
12 | PyObject *res = NULL; |
13 | char *p; |
14 | Py_ssize_t seqlen = 0; |
15 | Py_ssize_t sz = 0; |
16 | Py_ssize_t i, nbufs; |
17 | PyObject *seq, *item; |
18 | Py_buffer *buffers = NULL; |
19 | #define NB_STATIC_BUFFERS 108.57k |
20 | Py_buffer static_buffers[NB_STATIC_BUFFERS]; |
21 | #define GIL_THRESHOLD 10485768.57k |
22 | int drop_gil = 1; |
23 | PyThreadState *save = NULL; |
24 | |
25 | seq = PySequence_Fast(iterable, "can only join an iterable"); |
26 | if (seq == NULL) { Branch (26:9): [True: 1, False: 48]
Branch (26:9): [True: 1, False: 31.2k]
|
27 | return NULL; |
28 | } |
29 | |
30 | seqlen = PySequence_Fast_GET_SIZE(seq); |
31 | if (seqlen == 0) { Branch (31:9): [True: 2, False: 46]
Branch (31:9): [True: 3.69k, False: 27.5k]
|
32 | Py_DECREF(seq); |
33 | return STRINGLIB_NEW(NULL, 0); |
34 | } |
35 | #if !STRINGLIB_MUTABLE |
36 | if (seqlen == 1) { Branch (36:9): [True: 18.9k, False: 8.52k]
|
37 | item = PySequence_Fast_GET_ITEM(seq, 0); |
38 | if (STRINGLIB_CHECK_EXACT(item)) { |
39 | Py_INCREF(item); |
40 | Py_DECREF(seq); |
41 | return item; |
42 | } |
43 | } |
44 | #endif |
45 | if (seqlen > NB_STATIC_BUFFERS) { Branch (45:9): [True: 2, False: 44]
Branch (45:9): [True: 2.19k, False: 6.33k]
|
46 | buffers = PyMem_NEW(Py_buffer, seqlen); |
47 | if (buffers == NULL) { Branch (47:13): [True: 0, False: 2]
Branch (47:13): [True: 0, False: 2.19k]
|
48 | Py_DECREF(seq); |
49 | PyErr_NoMemory(); |
50 | return NULL; |
51 | } |
52 | } |
53 | else { |
54 | buffers = static_buffers; |
55 | } |
56 | |
57 | /* Here is the general case. Do a pre-pass to figure out the total |
58 | * amount of space we'll need (sz), and see whether all arguments are |
59 | * bytes-like. |
60 | */ |
61 | for (i = 0, nbufs = 0; 8.57k i < seqlen; i++1.53M ) { Branch (61:28): [True: 200k, False: 44]
Branch (61:28): [True: 1.33M, False: 8.52k]
|
62 | Py_ssize_t itemlen; |
63 | item = PySequence_Fast_GET_ITEM(seq, i); |
64 | if (PyBytes_CheckExact(item)) { |
65 | /* Fast path. */ |
66 | Py_INCREF(item); |
67 | buffers[i].obj = item; |
68 | buffers[i].buf = PyBytes_AS_STRING(item); |
69 | buffers[i].len = PyBytes_GET_SIZE(item); |
70 | } |
71 | else { |
72 | if (PyObject_GetBuffer(item, &buffers[i], PyBUF_SIMPLE) != 0) { Branch (72:17): [True: 2, False: 53]
Branch (72:17): [True: 3, False: 52]
|
73 | PyErr_Format(PyExc_TypeError, |
74 | "sequence item %zd: expected a bytes-like object, " |
75 | "%.80s found", |
76 | i, Py_TYPE(item)->tp_name); |
77 | goto error; |
78 | } |
79 | /* If the backing objects are mutable, then dropping the GIL |
80 | * opens up race conditions where another thread tries to modify |
81 | * the object which we hold a buffer on it. Such code has data |
82 | * races anyway, but this is a conservative approach that avoids |
83 | * changing the behaviour of that data race. |
84 | */ |
85 | drop_gil = 0; |
86 | } |
87 | nbufs = i + 1; /* for error cleanup */ |
88 | itemlen = buffers[i].len; |
89 | if (itemlen > PY_SSIZE_T_MAX - sz) { Branch (89:13): [True: 0, False: 200k]
Branch (89:13): [True: 0, False: 1.33M]
|
90 | PyErr_SetString(PyExc_OverflowError, |
91 | "join() result is too long"); |
92 | goto error; |
93 | } |
94 | sz += itemlen; |
95 | if (i != 0) { Branch (95:13): [True: 200k, False: 46]
Branch (95:13): [True: 1.32M, False: 8.52k]
|
96 | if (seplen > PY_SSIZE_T_MAX - sz) { Branch (96:17): [True: 0, False: 200k]
Branch (96:17): [True: 0, False: 1.32M]
|
97 | PyErr_SetString(PyExc_OverflowError, |
98 | "join() result is too long"); |
99 | goto error; |
100 | } |
101 | sz += seplen; |
102 | } |
103 | if (seqlen != PySequence_Fast_GET_SIZE(seq)) { Branch (103:13): [True: 0, False: 200k]
Branch (103:13): [True: 0, False: 1.33M]
|
104 | PyErr_SetString(PyExc_RuntimeError, |
105 | "sequence changed size during iteration"); |
106 | goto error; |
107 | } |
108 | } |
109 | |
110 | /* Allocate result space. */ |
111 | res = STRINGLIB_NEW(NULL, sz); |
112 | if (res == NULL) Branch (112:9): [True: 0, False: 44]
Branch (112:9): [True: 0, False: 8.52k]
|
113 | goto error; |
114 | |
115 | /* Catenate everything. */ |
116 | p = STRINGLIB_STR(res); |
117 | if (sz < GIL_THRESHOLD) { Branch (117:9): [True: 44, False: 0]
Branch (117:9): [True: 8.51k, False: 15]
|
118 | drop_gil = 0; /* Benefits are likely outweighed by the overheads */ |
119 | } |
120 | if (drop_gil) { Branch (120:9): [True: 0, False: 44]
Branch (120:9): [True: 15, False: 8.51k]
|
121 | save = PyEval_SaveThread(); |
122 | } |
123 | if (!seplen) { Branch (123:9): [True: 35, False: 9]
Branch (123:9): [True: 7.91k, False: 614]
|
124 | /* fast path */ |
125 | for (i = 0; i < nbufs; i++1.32M ) { Branch (125:21): [True: 100k, False: 35]
Branch (125:21): [True: 1.22M, False: 7.91k]
|
126 | Py_ssize_t n = buffers[i].len; |
127 | char *q = buffers[i].buf; |
128 | memcpy(p, q, n); |
129 | p += n; |
130 | } |
131 | } |
132 | else { |
133 | for (i = 0; i < nbufs; i++208k ) { Branch (133:21): [True: 100k, False: 9]
Branch (133:21): [True: 108k, False: 614]
|
134 | Py_ssize_t n; |
135 | char *q; |
136 | if (i) { Branch (136:17): [True: 100k, False: 9]
Branch (136:17): [True: 107k, False: 614]
|
137 | memcpy(p, sepstr, seplen); |
138 | p += seplen; |
139 | } |
140 | n = buffers[i].len; |
141 | q = buffers[i].buf; |
142 | memcpy(p, q, n); |
143 | p += n; |
144 | } |
145 | } |
146 | if (drop_gil) { Branch (146:9): [True: 0, False: 44]
Branch (146:9): [True: 15, False: 8.51k]
|
147 | PyEval_RestoreThread(save); |
148 | } |
149 | goto done; |
150 | |
151 | error: |
152 | res = NULL; |
153 | done: |
154 | Py_DECREF(seq); |
155 | for (i = 0; i < nbufs; i++1.53M ) Branch (155:17): [True: 200k, False: 46]
Branch (155:17): [True: 1.33M, False: 8.52k]
|
156 | PyBuffer_Release(&buffers[i]); |
157 | if (buffers != static_buffers) Branch (157:9): [True: 2, False: 44]
Branch (157:9): [True: 2.19k, False: 6.33k]
|
158 | PyMem_Free(buffers); |
159 | return res; |
160 | } bytearrayobject.c:stringlib_bytes_join Line | Count | Source | 9 | { | 10 | const char *sepstr = STRINGLIB_STR(sep); | 11 | Py_ssize_t seplen = STRINGLIB_LEN(sep); | 12 | PyObject *res = NULL; | 13 | char *p; | 14 | Py_ssize_t seqlen = 0; | 15 | Py_ssize_t sz = 0; | 16 | Py_ssize_t i, nbufs; | 17 | PyObject *seq, *item; | 18 | Py_buffer *buffers = NULL; | 19 | #define NB_STATIC_BUFFERS 10 | 20 | Py_buffer static_buffers[NB_STATIC_BUFFERS]; | 21 | #define GIL_THRESHOLD 1048576 | 22 | int drop_gil = 1; | 23 | PyThreadState *save = NULL; | 24 | | 25 | seq = PySequence_Fast(iterable, "can only join an iterable"); | 26 | if (seq == NULL) { Branch (26:9): [True: 1, False: 48]
| 27 | return NULL; | 28 | } | 29 | | 30 | seqlen = PySequence_Fast_GET_SIZE(seq); | 31 | if (seqlen == 0) { Branch (31:9): [True: 2, False: 46]
| 32 | Py_DECREF(seq); | 33 | return STRINGLIB_NEW(NULL, 0); | 34 | } | 35 | #if !STRINGLIB_MUTABLE | 36 | if (seqlen == 1) { | 37 | item = PySequence_Fast_GET_ITEM(seq, 0); | 38 | if (STRINGLIB_CHECK_EXACT(item)) { | 39 | Py_INCREF(item); | 40 | Py_DECREF(seq); | 41 | return item; | 42 | } | 43 | } | 44 | #endif | 45 | if (seqlen > NB_STATIC_BUFFERS) { Branch (45:9): [True: 2, False: 44]
| 46 | buffers = PyMem_NEW(Py_buffer, seqlen); | 47 | if (buffers == NULL) { Branch (47:13): [True: 0, False: 2]
| 48 | Py_DECREF(seq); | 49 | PyErr_NoMemory(); | 50 | return NULL; | 51 | } | 52 | } | 53 | else { | 54 | buffers = static_buffers; | 55 | } | 56 | | 57 | /* Here is the general case. Do a pre-pass to figure out the total | 58 | * amount of space we'll need (sz), and see whether all arguments are | 59 | * bytes-like. | 60 | */ | 61 | for (i = 0, nbufs = 0; 46 i < seqlen; i++200k ) { Branch (61:28): [True: 200k, False: 44]
| 62 | Py_ssize_t itemlen; | 63 | item = PySequence_Fast_GET_ITEM(seq, i); | 64 | if (PyBytes_CheckExact(item)) { | 65 | /* Fast path. */ | 66 | Py_INCREF(item); | 67 | buffers[i].obj = item; | 68 | buffers[i].buf = PyBytes_AS_STRING(item); | 69 | buffers[i].len = PyBytes_GET_SIZE(item); | 70 | } | 71 | else { | 72 | if (PyObject_GetBuffer(item, &buffers[i], PyBUF_SIMPLE) != 0) { Branch (72:17): [True: 2, False: 53]
| 73 | PyErr_Format(PyExc_TypeError, | 74 | "sequence item %zd: expected a bytes-like object, " | 75 | "%.80s found", | 76 | i, Py_TYPE(item)->tp_name); | 77 | goto error; | 78 | } | 79 | /* If the backing objects are mutable, then dropping the GIL | 80 | * opens up race conditions where another thread tries to modify | 81 | * the object which we hold a buffer on it. Such code has data | 82 | * races anyway, but this is a conservative approach that avoids | 83 | * changing the behaviour of that data race. | 84 | */ | 85 | drop_gil = 0; | 86 | } | 87 | nbufs = i + 1; /* for error cleanup */ | 88 | itemlen = buffers[i].len; | 89 | if (itemlen > PY_SSIZE_T_MAX - sz) { Branch (89:13): [True: 0, False: 200k]
| 90 | PyErr_SetString(PyExc_OverflowError, | 91 | "join() result is too long"); | 92 | goto error; | 93 | } | 94 | sz += itemlen; | 95 | if (i != 0) { Branch (95:13): [True: 200k, False: 46]
| 96 | if (seplen > PY_SSIZE_T_MAX - sz) { Branch (96:17): [True: 0, False: 200k]
| 97 | PyErr_SetString(PyExc_OverflowError, | 98 | "join() result is too long"); | 99 | goto error; | 100 | } | 101 | sz += seplen; | 102 | } | 103 | if (seqlen != PySequence_Fast_GET_SIZE(seq)) { Branch (103:13): [True: 0, False: 200k]
| 104 | PyErr_SetString(PyExc_RuntimeError, | 105 | "sequence changed size during iteration"); | 106 | goto error; | 107 | } | 108 | } | 109 | | 110 | /* Allocate result space. */ | 111 | res = STRINGLIB_NEW(NULL, sz); | 112 | if (res == NULL) Branch (112:9): [True: 0, False: 44]
| 113 | goto error; | 114 | | 115 | /* Catenate everything. */ | 116 | p = STRINGLIB_STR(res); | 117 | if (sz < GIL_THRESHOLD) { Branch (117:9): [True: 44, False: 0]
| 118 | drop_gil = 0; /* Benefits are likely outweighed by the overheads */ | 119 | } | 120 | if (drop_gil) { Branch (120:9): [True: 0, False: 44]
| 121 | save = PyEval_SaveThread(); | 122 | } | 123 | if (!seplen) { Branch (123:9): [True: 35, False: 9]
| 124 | /* fast path */ | 125 | for (i = 0; i < nbufs; i++100k ) { Branch (125:21): [True: 100k, False: 35]
| 126 | Py_ssize_t n = buffers[i].len; | 127 | char *q = buffers[i].buf; | 128 | memcpy(p, q, n); | 129 | p += n; | 130 | } | 131 | } | 132 | else { | 133 | for (i = 0; i < nbufs; i++100k ) { Branch (133:21): [True: 100k, False: 9]
| 134 | Py_ssize_t n; | 135 | char *q; | 136 | if (i) { Branch (136:17): [True: 100k, False: 9]
| 137 | memcpy(p, sepstr, seplen); | 138 | p += seplen; | 139 | } | 140 | n = buffers[i].len; | 141 | q = buffers[i].buf; | 142 | memcpy(p, q, n); | 143 | p += n; | 144 | } | 145 | } | 146 | if (drop_gil) { Branch (146:9): [True: 0, False: 44]
| 147 | PyEval_RestoreThread(save); | 148 | } | 149 | goto done; | 150 | | 151 | error: | 152 | res = NULL; | 153 | done: | 154 | Py_DECREF(seq); | 155 | for (i = 0; i < nbufs; i++200k ) Branch (155:17): [True: 200k, False: 46]
| 156 | PyBuffer_Release(&buffers[i]); | 157 | if (buffers != static_buffers) Branch (157:9): [True: 2, False: 44]
| 158 | PyMem_Free(buffers); | 159 | return res; | 160 | } |
bytesobject.c:stringlib_bytes_join Line | Count | Source | 9 | { | 10 | const char *sepstr = STRINGLIB_STR(sep); | 11 | Py_ssize_t seplen = STRINGLIB_LEN(sep); | 12 | PyObject *res = NULL; | 13 | char *p; | 14 | Py_ssize_t seqlen = 0; | 15 | Py_ssize_t sz = 0; | 16 | Py_ssize_t i, nbufs; | 17 | PyObject *seq, *item; | 18 | Py_buffer *buffers = NULL; | 19 | #define NB_STATIC_BUFFERS 10 | 20 | Py_buffer static_buffers[NB_STATIC_BUFFERS]; | 21 | #define GIL_THRESHOLD 1048576 | 22 | int drop_gil = 1; | 23 | PyThreadState *save = NULL; | 24 | | 25 | seq = PySequence_Fast(iterable, "can only join an iterable"); | 26 | if (seq == NULL) { Branch (26:9): [True: 1, False: 31.2k]
| 27 | return NULL; | 28 | } | 29 | | 30 | seqlen = PySequence_Fast_GET_SIZE(seq); | 31 | if (seqlen == 0) { Branch (31:9): [True: 3.69k, False: 27.5k]
| 32 | Py_DECREF(seq); | 33 | return STRINGLIB_NEW(NULL, 0); | 34 | } | 35 | #if !STRINGLIB_MUTABLE | 36 | if (seqlen == 1) { Branch (36:9): [True: 18.9k, False: 8.52k]
| 37 | item = PySequence_Fast_GET_ITEM(seq, 0); | 38 | if (STRINGLIB_CHECK_EXACT(item)) { | 39 | Py_INCREF(item); | 40 | Py_DECREF(seq); | 41 | return item; | 42 | } | 43 | } | 44 | #endif | 45 | if (seqlen > NB_STATIC_BUFFERS) { Branch (45:9): [True: 2.19k, False: 6.33k]
| 46 | buffers = PyMem_NEW(Py_buffer, seqlen); | 47 | if (buffers == NULL) { Branch (47:13): [True: 0, False: 2.19k]
| 48 | Py_DECREF(seq); | 49 | PyErr_NoMemory(); | 50 | return NULL; | 51 | } | 52 | } | 53 | else { | 54 | buffers = static_buffers; | 55 | } | 56 | | 57 | /* Here is the general case. Do a pre-pass to figure out the total | 58 | * amount of space we'll need (sz), and see whether all arguments are | 59 | * bytes-like. | 60 | */ | 61 | for (i = 0, nbufs = 0; 8.52k i < seqlen; i++1.33M ) { Branch (61:28): [True: 1.33M, False: 8.52k]
| 62 | Py_ssize_t itemlen; | 63 | item = PySequence_Fast_GET_ITEM(seq, i); | 64 | if (PyBytes_CheckExact(item)) { | 65 | /* Fast path. */ | 66 | Py_INCREF(item); | 67 | buffers[i].obj = item; | 68 | buffers[i].buf = PyBytes_AS_STRING(item); | 69 | buffers[i].len = PyBytes_GET_SIZE(item); | 70 | } | 71 | else { | 72 | if (PyObject_GetBuffer(item, &buffers[i], PyBUF_SIMPLE) != 0) { Branch (72:17): [True: 3, False: 52]
| 73 | PyErr_Format(PyExc_TypeError, | 74 | "sequence item %zd: expected a bytes-like object, " | 75 | "%.80s found", | 76 | i, Py_TYPE(item)->tp_name); | 77 | goto error; | 78 | } | 79 | /* If the backing objects are mutable, then dropping the GIL | 80 | * opens up race conditions where another thread tries to modify | 81 | * the object which we hold a buffer on it. Such code has data | 82 | * races anyway, but this is a conservative approach that avoids | 83 | * changing the behaviour of that data race. | 84 | */ | 85 | drop_gil = 0; | 86 | } | 87 | nbufs = i + 1; /* for error cleanup */ | 88 | itemlen = buffers[i].len; | 89 | if (itemlen > PY_SSIZE_T_MAX - sz) { Branch (89:13): [True: 0, False: 1.33M]
| 90 | PyErr_SetString(PyExc_OverflowError, | 91 | "join() result is too long"); | 92 | goto error; | 93 | } | 94 | sz += itemlen; | 95 | if (i != 0) { Branch (95:13): [True: 1.32M, False: 8.52k]
| 96 | if (seplen > PY_SSIZE_T_MAX - sz) { Branch (96:17): [True: 0, False: 1.32M]
| 97 | PyErr_SetString(PyExc_OverflowError, | 98 | "join() result is too long"); | 99 | goto error; | 100 | } | 101 | sz += seplen; | 102 | } | 103 | if (seqlen != PySequence_Fast_GET_SIZE(seq)) { Branch (103:13): [True: 0, False: 1.33M]
| 104 | PyErr_SetString(PyExc_RuntimeError, | 105 | "sequence changed size during iteration"); | 106 | goto error; | 107 | } | 108 | } | 109 | | 110 | /* Allocate result space. */ | 111 | res = STRINGLIB_NEW(NULL, sz); | 112 | if (res == NULL) Branch (112:9): [True: 0, False: 8.52k]
| 113 | goto error; | 114 | | 115 | /* Catenate everything. */ | 116 | p = STRINGLIB_STR(res); | 117 | if (sz < GIL_THRESHOLD) { Branch (117:9): [True: 8.51k, False: 15]
| 118 | drop_gil = 0; /* Benefits are likely outweighed by the overheads */ | 119 | } | 120 | if (drop_gil) { Branch (120:9): [True: 15, False: 8.51k]
| 121 | save = PyEval_SaveThread(); | 122 | } | 123 | if (!seplen) { Branch (123:9): [True: 7.91k, False: 614]
| 124 | /* fast path */ | 125 | for (i = 0; i < nbufs; i++1.22M ) { Branch (125:21): [True: 1.22M, False: 7.91k]
| 126 | Py_ssize_t n = buffers[i].len; | 127 | char *q = buffers[i].buf; | 128 | memcpy(p, q, n); | 129 | p += n; | 130 | } | 131 | } | 132 | else { | 133 | for (i = 0; i < nbufs; i++108k ) { Branch (133:21): [True: 108k, False: 614]
| 134 | Py_ssize_t n; | 135 | char *q; | 136 | if (i) { Branch (136:17): [True: 107k, False: 614]
| 137 | memcpy(p, sepstr, seplen); | 138 | p += seplen; | 139 | } | 140 | n = buffers[i].len; | 141 | q = buffers[i].buf; | 142 | memcpy(p, q, n); | 143 | p += n; | 144 | } | 145 | } | 146 | if (drop_gil) { Branch (146:9): [True: 15, False: 8.51k]
| 147 | PyEval_RestoreThread(save); | 148 | } | 149 | goto done; | 150 | | 151 | error: | 152 | res = NULL; | 153 | done: | 154 | Py_DECREF(seq); | 155 | for (i = 0; i < nbufs; i++1.33M ) Branch (155:17): [True: 1.33M, False: 8.52k]
| 156 | PyBuffer_Release(&buffers[i]); | 157 | if (buffers != static_buffers) Branch (157:9): [True: 2.19k, False: 6.33k]
| 158 | PyMem_Free(buffers); | 159 | return res; | 160 | } |
|
161 | |
162 | #undef NB_STATIC_BUFFERS |
163 | #undef GIL_THRESHOLD |