Coverage Report

Created: 2022-07-08 09:39

/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