Coverage Report

Created: 2025-07-11 06:59

/src/Python-3.8.3/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
1
{
10
1
    char *sepstr = STRINGLIB_STR(sep);
11
1
    const Py_ssize_t seplen = STRINGLIB_LEN(sep);
12
1
    PyObject *res = NULL;
13
1
    char *p;
14
1
    Py_ssize_t seqlen = 0;
15
1
    Py_ssize_t sz = 0;
16
1
    Py_ssize_t i, nbufs;
17
1
    PyObject *seq, *item;
18
1
    Py_buffer *buffers = NULL;
19
1
#define NB_STATIC_BUFFERS 10
20
1
    Py_buffer static_buffers[NB_STATIC_BUFFERS];
21
22
1
    seq = PySequence_Fast(iterable, "can only join an iterable");
23
1
    if (seq == NULL) {
24
0
        return NULL;
25
0
    }
26
27
1
    seqlen = PySequence_Fast_GET_SIZE(seq);
28
1
    if (seqlen == 0) {
29
0
        Py_DECREF(seq);
30
0
        return STRINGLIB_NEW(NULL, 0);
31
0
    }
32
#ifndef STRINGLIB_MUTABLE
33
1
    if (seqlen == 1) {
34
1
        item = PySequence_Fast_GET_ITEM(seq, 0);
35
1
        if (STRINGLIB_CHECK_EXACT(item)) {
36
1
            Py_INCREF(item);
37
1
            Py_DECREF(seq);
38
1
            return item;
39
1
        }
40
1
    }
41
0
#endif
42
0
    if (seqlen > NB_STATIC_BUFFERS) {
43
0
        buffers = PyMem_NEW(Py_buffer, seqlen);
44
0
        if (buffers == NULL) {
45
0
            Py_DECREF(seq);
46
0
            PyErr_NoMemory();
47
0
            return NULL;
48
0
        }
49
0
    }
50
0
    else {
51
0
        buffers = static_buffers;
52
0
    }
53
54
    /* Here is the general case.  Do a pre-pass to figure out the total
55
     * amount of space we'll need (sz), and see whether all arguments are
56
     * bytes-like.
57
     */
58
0
    for (i = 0, nbufs = 0; i < seqlen; i++) {
59
0
        Py_ssize_t itemlen;
60
0
        item = PySequence_Fast_GET_ITEM(seq, i);
61
0
        if (PyBytes_CheckExact(item)) {
62
            /* Fast path. */
63
0
            Py_INCREF(item);
64
0
            buffers[i].obj = item;
65
0
            buffers[i].buf = PyBytes_AS_STRING(item);
66
0
            buffers[i].len = PyBytes_GET_SIZE(item);
67
0
        }
68
0
        else if (PyObject_GetBuffer(item, &buffers[i], PyBUF_SIMPLE) != 0) {
69
0
            PyErr_Format(PyExc_TypeError,
70
0
                         "sequence item %zd: expected a bytes-like object, "
71
0
                         "%.80s found",
72
0
                         i, Py_TYPE(item)->tp_name);
73
0
            goto error;
74
0
        }
75
0
        nbufs = i + 1;  /* for error cleanup */
76
0
        itemlen = buffers[i].len;
77
0
        if (itemlen > PY_SSIZE_T_MAX - sz) {
78
0
            PyErr_SetString(PyExc_OverflowError,
79
0
                            "join() result is too long");
80
0
            goto error;
81
0
        }
82
0
        sz += itemlen;
83
0
        if (i != 0) {
84
0
            if (seplen > PY_SSIZE_T_MAX - sz) {
85
0
                PyErr_SetString(PyExc_OverflowError,
86
0
                                "join() result is too long");
87
0
                goto error;
88
0
            }
89
0
            sz += seplen;
90
0
        }
91
0
        if (seqlen != PySequence_Fast_GET_SIZE(seq)) {
92
0
            PyErr_SetString(PyExc_RuntimeError,
93
0
                            "sequence changed size during iteration");
94
0
            goto error;
95
0
        }
96
0
    }
97
98
    /* Allocate result space. */
99
0
    res = STRINGLIB_NEW(NULL, sz);
100
0
    if (res == NULL)
101
0
        goto error;
102
103
    /* Catenate everything. */
104
0
    p = STRINGLIB_STR(res);
105
0
    if (!seplen) {
106
        /* fast path */
107
0
        for (i = 0; i < nbufs; i++) {
108
0
            Py_ssize_t n = buffers[i].len;
109
0
            char *q = buffers[i].buf;
110
0
            memcpy(p, q, n);
111
0
            p += n;
112
0
        }
113
0
        goto done;
114
0
    }
115
0
    for (i = 0; i < nbufs; i++) {
116
0
        Py_ssize_t n;
117
0
        char *q;
118
0
        if (i) {
119
0
            memcpy(p, sepstr, seplen);
120
0
            p += seplen;
121
0
        }
122
0
        n = buffers[i].len;
123
0
        q = buffers[i].buf;
124
0
        memcpy(p, q, n);
125
0
        p += n;
126
0
    }
127
0
    goto done;
128
129
0
error:
130
0
    res = NULL;
131
0
done:
132
0
    Py_DECREF(seq);
133
0
    for (i = 0; i < nbufs; i++)
134
0
        PyBuffer_Release(&buffers[i]);
135
0
    if (buffers != static_buffers)
136
0
        PyMem_FREE(buffers);
137
0
    return res;
138
0
}
Unexecuted instantiation: bytearrayobject.c:stringlib_bytes_join
bytesobject.c:stringlib_bytes_join
Line
Count
Source
9
1
{
10
1
    char *sepstr = STRINGLIB_STR(sep);
11
1
    const Py_ssize_t seplen = STRINGLIB_LEN(sep);
12
1
    PyObject *res = NULL;
13
1
    char *p;
14
1
    Py_ssize_t seqlen = 0;
15
1
    Py_ssize_t sz = 0;
16
1
    Py_ssize_t i, nbufs;
17
1
    PyObject *seq, *item;
18
1
    Py_buffer *buffers = NULL;
19
1
#define NB_STATIC_BUFFERS 10
20
1
    Py_buffer static_buffers[NB_STATIC_BUFFERS];
21
22
1
    seq = PySequence_Fast(iterable, "can only join an iterable");
23
1
    if (seq == NULL) {
24
0
        return NULL;
25
0
    }
26
27
1
    seqlen = PySequence_Fast_GET_SIZE(seq);
28
1
    if (seqlen == 0) {
29
0
        Py_DECREF(seq);
30
0
        return STRINGLIB_NEW(NULL, 0);
31
0
    }
32
1
#ifndef STRINGLIB_MUTABLE
33
1
    if (seqlen == 1) {
34
1
        item = PySequence_Fast_GET_ITEM(seq, 0);
35
1
        if (STRINGLIB_CHECK_EXACT(item)) {
36
1
            Py_INCREF(item);
37
1
            Py_DECREF(seq);
38
1
            return item;
39
1
        }
40
1
    }
41
0
#endif
42
0
    if (seqlen > NB_STATIC_BUFFERS) {
43
0
        buffers = PyMem_NEW(Py_buffer, seqlen);
44
0
        if (buffers == NULL) {
45
0
            Py_DECREF(seq);
46
0
            PyErr_NoMemory();
47
0
            return NULL;
48
0
        }
49
0
    }
50
0
    else {
51
0
        buffers = static_buffers;
52
0
    }
53
54
    /* Here is the general case.  Do a pre-pass to figure out the total
55
     * amount of space we'll need (sz), and see whether all arguments are
56
     * bytes-like.
57
     */
58
0
    for (i = 0, nbufs = 0; i < seqlen; i++) {
59
0
        Py_ssize_t itemlen;
60
0
        item = PySequence_Fast_GET_ITEM(seq, i);
61
0
        if (PyBytes_CheckExact(item)) {
62
            /* Fast path. */
63
0
            Py_INCREF(item);
64
0
            buffers[i].obj = item;
65
0
            buffers[i].buf = PyBytes_AS_STRING(item);
66
0
            buffers[i].len = PyBytes_GET_SIZE(item);
67
0
        }
68
0
        else if (PyObject_GetBuffer(item, &buffers[i], PyBUF_SIMPLE) != 0) {
69
0
            PyErr_Format(PyExc_TypeError,
70
0
                         "sequence item %zd: expected a bytes-like object, "
71
0
                         "%.80s found",
72
0
                         i, Py_TYPE(item)->tp_name);
73
0
            goto error;
74
0
        }
75
0
        nbufs = i + 1;  /* for error cleanup */
76
0
        itemlen = buffers[i].len;
77
0
        if (itemlen > PY_SSIZE_T_MAX - sz) {
78
0
            PyErr_SetString(PyExc_OverflowError,
79
0
                            "join() result is too long");
80
0
            goto error;
81
0
        }
82
0
        sz += itemlen;
83
0
        if (i != 0) {
84
0
            if (seplen > PY_SSIZE_T_MAX - sz) {
85
0
                PyErr_SetString(PyExc_OverflowError,
86
0
                                "join() result is too long");
87
0
                goto error;
88
0
            }
89
0
            sz += seplen;
90
0
        }
91
0
        if (seqlen != PySequence_Fast_GET_SIZE(seq)) {
92
0
            PyErr_SetString(PyExc_RuntimeError,
93
0
                            "sequence changed size during iteration");
94
0
            goto error;
95
0
        }
96
0
    }
97
98
    /* Allocate result space. */
99
0
    res = STRINGLIB_NEW(NULL, sz);
100
0
    if (res == NULL)
101
0
        goto error;
102
103
    /* Catenate everything. */
104
0
    p = STRINGLIB_STR(res);
105
0
    if (!seplen) {
106
        /* fast path */
107
0
        for (i = 0; i < nbufs; i++) {
108
0
            Py_ssize_t n = buffers[i].len;
109
0
            char *q = buffers[i].buf;
110
0
            memcpy(p, q, n);
111
0
            p += n;
112
0
        }
113
0
        goto done;
114
0
    }
115
0
    for (i = 0; i < nbufs; i++) {
116
0
        Py_ssize_t n;
117
0
        char *q;
118
0
        if (i) {
119
0
            memcpy(p, sepstr, seplen);
120
0
            p += seplen;
121
0
        }
122
0
        n = buffers[i].len;
123
0
        q = buffers[i].buf;
124
0
        memcpy(p, q, n);
125
0
        p += n;
126
0
    }
127
0
    goto done;
128
129
0
error:
130
0
    res = NULL;
131
0
done:
132
0
    Py_DECREF(seq);
133
0
    for (i = 0; i < nbufs; i++)
134
0
        PyBuffer_Release(&buffers[i]);
135
0
    if (buffers != static_buffers)
136
0
        PyMem_FREE(buffers);
137
0
    return res;
138
0
}
139
140
#undef NB_STATIC_BUFFERS