Branch data Line data Source code
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 : 71042 : STRINGLIB(bytes_join)(PyObject *sep, PyObject *iterable) 9 : : { 10 : 71042 : const char *sepstr = STRINGLIB_STR(sep); 11 : 71042 : Py_ssize_t seplen = STRINGLIB_LEN(sep); 12 : 71042 : PyObject *res = NULL; 13 : : char *p; 14 : 71042 : Py_ssize_t seqlen = 0; 15 : 71042 : Py_ssize_t sz = 0; 16 : : Py_ssize_t i, nbufs; 17 : : PyObject *seq, *item; 18 : 71042 : Py_buffer *buffers = NULL; 19 : : #define NB_STATIC_BUFFERS 10 20 : : Py_buffer static_buffers[NB_STATIC_BUFFERS]; 21 : : #define GIL_THRESHOLD 1048576 22 : 71042 : int drop_gil = 1; 23 : 71042 : PyThreadState *save = NULL; 24 : : 25 : 71042 : seq = PySequence_Fast(iterable, "can only join an iterable"); 26 [ + + ]: 71042 : if (seq == NULL) { 27 : 2 : return NULL; 28 : : } 29 : : 30 [ + + ]: 71040 : seqlen = PySequence_Fast_GET_SIZE(seq); 31 [ + + ]: 71040 : if (seqlen == 0) { 32 : 3806 : Py_DECREF(seq); 33 : 3806 : return STRINGLIB_NEW(NULL, 0); 34 : : } 35 : : #if !STRINGLIB_MUTABLE 36 [ + + ]: 67188 : if (seqlen == 1) { 37 [ + + ]: 29527 : item = PySequence_Fast_GET_ITEM(seq, 0); 38 [ + + ]: 29527 : if (STRINGLIB_CHECK_EXACT(item)) { 39 : 29525 : Py_INCREF(item); 40 : 29525 : Py_DECREF(seq); 41 : 29525 : return item; 42 : : } 43 : : } 44 : : #endif 45 [ + + ]: 37709 : if (seqlen > NB_STATIC_BUFFERS) { 46 [ + - ]: 2364 : buffers = PyMem_NEW(Py_buffer, seqlen); 47 [ - + ]: 2364 : if (buffers == NULL) { 48 : 0 : Py_DECREF(seq); 49 : : PyErr_NoMemory(); 50 : 0 : return NULL; 51 : : } 52 : : } 53 : : else { 54 : 35345 : 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 [ + + ]: 1340040 : for (i = 0, nbufs = 0; i < seqlen; i++) { 62 : : Py_ssize_t itemlen; 63 [ + + ]: 1302336 : item = PySequence_Fast_GET_ITEM(seq, i); 64 [ + + ]: 1302336 : if (PyBytes_CheckExact(item)) { 65 : : /* Fast path. */ 66 : 1302226 : Py_INCREF(item); 67 : 1302226 : buffers[i].obj = item; 68 : 1302226 : buffers[i].buf = PyBytes_AS_STRING(item); 69 : 1302226 : buffers[i].len = PyBytes_GET_SIZE(item); 70 : : } 71 : : else { 72 [ + + ]: 110 : if (PyObject_GetBuffer(item, &buffers[i], PyBUF_SIMPLE) != 0) { 73 : 5 : PyErr_Format(PyExc_TypeError, 74 : : "sequence item %zd: expected a bytes-like object, " 75 : : "%.80s found", 76 : 5 : i, Py_TYPE(item)->tp_name); 77 : 5 : 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 : 105 : drop_gil = 0; 86 : : } 87 : 1302331 : nbufs = i + 1; /* for error cleanup */ 88 : 1302331 : itemlen = buffers[i].len; 89 [ - + ]: 1302331 : if (itemlen > PY_SSIZE_T_MAX - sz) { 90 : 0 : PyErr_SetString(PyExc_OverflowError, 91 : : "join() result is too long"); 92 : 0 : goto error; 93 : : } 94 : 1302331 : sz += itemlen; 95 [ + + ]: 1302331 : if (i != 0) { 96 [ - + ]: 1264623 : if (seplen > PY_SSIZE_T_MAX - sz) { 97 : 0 : PyErr_SetString(PyExc_OverflowError, 98 : : "join() result is too long"); 99 : 0 : goto error; 100 : : } 101 : 1264623 : sz += seplen; 102 : : } 103 [ + + - + ]: 1302331 : if (seqlen != PySequence_Fast_GET_SIZE(seq)) { 104 : 0 : PyErr_SetString(PyExc_RuntimeError, 105 : : "sequence changed size during iteration"); 106 : 0 : goto error; 107 : : } 108 : : } 109 : : 110 : : /* Allocate result space. */ 111 : 37704 : res = STRINGLIB_NEW(NULL, sz); 112 [ - + ]: 37704 : if (res == NULL) 113 : 0 : goto error; 114 : : 115 : : /* Catenate everything. */ 116 : 37704 : p = STRINGLIB_STR(res); 117 [ + + ]: 37704 : if (sz < GIL_THRESHOLD) { 118 : 37686 : drop_gil = 0; /* Benefits are likely outweighed by the overheads */ 119 : : } 120 [ + + ]: 37704 : if (drop_gil) { 121 : 18 : save = PyEval_SaveThread(); 122 : : } 123 [ + + ]: 37704 : if (!seplen) { 124 : : /* fast path */ 125 [ + + ]: 1130739 : for (i = 0; i < nbufs; i++) { 126 : 1093713 : Py_ssize_t n = buffers[i].len; 127 : 1093713 : char *q = buffers[i].buf; 128 : 1093713 : memcpy(p, q, n); 129 : 1093713 : p += n; 130 : : } 131 : : } 132 : : else { 133 [ + + ]: 209292 : for (i = 0; i < nbufs; i++) { 134 : : Py_ssize_t n; 135 : : char *q; 136 [ + + ]: 208614 : if (i) { 137 : 207936 : memcpy(p, sepstr, seplen); 138 : 207936 : p += seplen; 139 : : } 140 : 208614 : n = buffers[i].len; 141 : 208614 : q = buffers[i].buf; 142 : 208614 : memcpy(p, q, n); 143 : 208614 : p += n; 144 : : } 145 : : } 146 [ + + ]: 37704 : if (drop_gil) { 147 : 18 : PyEval_RestoreThread(save); 148 : : } 149 : 37704 : goto done; 150 : : 151 : 5 : error: 152 : 5 : res = NULL; 153 : 37709 : done: 154 : 37709 : Py_DECREF(seq); 155 [ + + ]: 1340040 : for (i = 0; i < nbufs; i++) 156 : 1302331 : PyBuffer_Release(&buffers[i]); 157 [ + + ]: 37709 : if (buffers != static_buffers) 158 : 2364 : PyMem_Free(buffers); 159 : 37709 : return res; 160 : : } 161 : : 162 : : #undef NB_STATIC_BUFFERS 163 : : #undef GIL_THRESHOLD