Branch data Line data Source code
1 : : #if STRINGLIB_IS_UNICODE
2 : : # error "transmogrify.h only compatible with byte-wise strings"
3 : : #endif
4 : :
5 : : /* the more complicated methods. parts of these should be pulled out into the
6 : : shared code in bytes_methods.c to cut down on duplicate code bloat. */
7 : :
8 : : /*[clinic input]
9 : : class B "PyObject *" "&PyType_Type"
10 : : [clinic start generated code]*/
11 : : /*[clinic end generated code: output=da39a3ee5e6b4b0d input=2935558188d97c76]*/
12 : :
13 : : #include "clinic/transmogrify.h.h"
14 : :
15 : : static inline PyObject *
16 : 160194 : return_self(PyObject *self)
17 : : {
18 : : #if !STRINGLIB_MUTABLE
19 [ + + ]: 99209 : if (STRINGLIB_CHECK_EXACT(self)) {
20 : 99163 : Py_INCREF(self);
21 : 99163 : return self;
22 : : }
23 : : #endif
24 : 61031 : return STRINGLIB_NEW(STRINGLIB_STR(self), STRINGLIB_LEN(self));
25 : : }
26 : :
27 : : /*[clinic input]
28 : : B.expandtabs as stringlib_expandtabs
29 : :
30 : : tabsize: int = 8
31 : :
32 : : Return a copy where all tab characters are expanded using spaces.
33 : :
34 : : If tabsize is not given, a tab size of 8 characters is assumed.
35 : : [clinic start generated code]*/
36 : :
37 : : static PyObject *
38 : 24 : stringlib_expandtabs_impl(PyObject *self, int tabsize)
39 : : /*[clinic end generated code: output=069cb7fae72e4c2b input=3c6d3b12aa3ccbea]*/
40 : : {
41 : : const char *e, *p;
42 : : char *q;
43 : : Py_ssize_t i, j;
44 : : PyObject *u;
45 : :
46 : : /* First pass: determine size of output string */
47 : 24 : i = j = 0;
48 : 24 : e = STRINGLIB_STR(self) + STRINGLIB_LEN(self);
49 [ + + ]: 20900 : for (p = STRINGLIB_STR(self); p < e; p++) {
50 [ + + ]: 20876 : if (*p == '\t') {
51 [ + - ]: 1322 : if (tabsize > 0) {
52 : 1322 : Py_ssize_t incr = tabsize - (j % tabsize);
53 [ - + ]: 1322 : if (j > PY_SSIZE_T_MAX - incr)
54 : 0 : goto overflow;
55 : 1322 : j += incr;
56 : : }
57 : : }
58 : : else {
59 [ - + ]: 19554 : if (j > PY_SSIZE_T_MAX - 1)
60 : 0 : goto overflow;
61 : 19554 : j++;
62 [ + + + + ]: 19554 : if (*p == '\n' || *p == '\r') {
63 [ - + ]: 54 : if (i > PY_SSIZE_T_MAX - j)
64 : 0 : goto overflow;
65 : 54 : i += j;
66 : 54 : j = 0;
67 : : }
68 : : }
69 : : }
70 : :
71 [ - + ]: 24 : if (i > PY_SSIZE_T_MAX - j)
72 : 0 : goto overflow;
73 : :
74 : : /* Second pass: create output string and fill it */
75 : 24 : u = STRINGLIB_NEW(NULL, i + j);
76 [ - + ]: 24 : if (!u)
77 : 0 : return NULL;
78 : :
79 : 24 : j = 0;
80 : 24 : q = STRINGLIB_STR(u);
81 : :
82 [ + + ]: 20900 : for (p = STRINGLIB_STR(self); p < e; p++) {
83 [ + + ]: 20876 : if (*p == '\t') {
84 [ + - ]: 1322 : if (tabsize > 0) {
85 : 1322 : i = tabsize - (j % tabsize);
86 : 1322 : j += i;
87 [ + + ]: 2772 : while (i--)
88 : 1450 : *q++ = ' ';
89 : : }
90 : : }
91 : : else {
92 : 19554 : j++;
93 : 19554 : *q++ = *p;
94 [ + + + + ]: 19554 : if (*p == '\n' || *p == '\r')
95 : 54 : j = 0;
96 : : }
97 : : }
98 : :
99 : 24 : return u;
100 : 0 : overflow:
101 : 0 : PyErr_SetString(PyExc_OverflowError, "result too long");
102 : 0 : return NULL;
103 : : }
104 : :
105 : : static inline PyObject *
106 : 114 : pad(PyObject *self, Py_ssize_t left, Py_ssize_t right, char fill)
107 : : {
108 : : PyObject *u;
109 : :
110 [ - + ]: 114 : if (left < 0)
111 : 0 : left = 0;
112 [ - + ]: 114 : if (right < 0)
113 : 0 : right = 0;
114 : :
115 [ + + - + ]: 114 : if (left == 0 && right == 0) {
116 : 0 : return return_self(self);
117 : : }
118 : :
119 : 114 : u = STRINGLIB_NEW(NULL, left + STRINGLIB_LEN(self) + right);
120 [ + - ]: 114 : if (u) {
121 [ + + ]: 114 : if (left)
122 : 52 : memset(STRINGLIB_STR(u), fill, left);
123 : 114 : memcpy(STRINGLIB_STR(u) + left,
124 : 114 : STRINGLIB_STR(self),
125 : 114 : STRINGLIB_LEN(self));
126 [ + + ]: 114 : if (right)
127 : 74 : memset(STRINGLIB_STR(u) + left + STRINGLIB_LEN(self),
128 : : fill, right);
129 : : }
130 : :
131 : 114 : return u;
132 : : }
133 : :
134 : : /*[clinic input]
135 : : B.ljust as stringlib_ljust
136 : :
137 : : width: Py_ssize_t
138 : : fillchar: char = b' '
139 : : /
140 : :
141 : : Return a left-justified string of length width.
142 : :
143 : : Padding is done using the specified fill character.
144 : : [clinic start generated code]*/
145 : :
146 : : static PyObject *
147 : 69 : stringlib_ljust_impl(PyObject *self, Py_ssize_t width, char fillchar)
148 : : /*[clinic end generated code: output=c79ca173c5ff8337 input=eff2d014bc7d80df]*/
149 : : {
150 [ + + ]: 69 : if (STRINGLIB_LEN(self) >= width) {
151 : 7 : return return_self(self);
152 : : }
153 : :
154 : 62 : return pad(self, 0, width - STRINGLIB_LEN(self), fillchar);
155 : : }
156 : :
157 : :
158 : : /*[clinic input]
159 : : B.rjust as stringlib_rjust
160 : :
161 : : width: Py_ssize_t
162 : : fillchar: char = b' '
163 : : /
164 : :
165 : : Return a right-justified string of length width.
166 : :
167 : : Padding is done using the specified fill character.
168 : : [clinic start generated code]*/
169 : :
170 : : static PyObject *
171 : 47 : stringlib_rjust_impl(PyObject *self, Py_ssize_t width, char fillchar)
172 : : /*[clinic end generated code: output=7df5d728a5439570 input=218b0bd31308955d]*/
173 : : {
174 [ + + ]: 47 : if (STRINGLIB_LEN(self) >= width) {
175 : 19 : return return_self(self);
176 : : }
177 : :
178 : 28 : return pad(self, width - STRINGLIB_LEN(self), 0, fillchar);
179 : : }
180 : :
181 : :
182 : : /*[clinic input]
183 : : B.center as stringlib_center
184 : :
185 : : width: Py_ssize_t
186 : : fillchar: char = b' '
187 : : /
188 : :
189 : : Return a centered string of length width.
190 : :
191 : : Padding is done using the specified fill character.
192 : : [clinic start generated code]*/
193 : :
194 : : static PyObject *
195 : 19 : stringlib_center_impl(PyObject *self, Py_ssize_t width, char fillchar)
196 : : /*[clinic end generated code: output=d8da2e055288b4c2 input=3776fd278765d89b]*/
197 : : {
198 : : Py_ssize_t marg, left;
199 : :
200 [ + + ]: 19 : if (STRINGLIB_LEN(self) >= width) {
201 : 7 : return return_self(self);
202 : : }
203 : :
204 : 12 : marg = width - STRINGLIB_LEN(self);
205 : 12 : left = marg / 2 + (marg & width & 1);
206 : :
207 : 12 : return pad(self, left, marg - left, fillchar);
208 : : }
209 : :
210 : : /*[clinic input]
211 : : B.zfill as stringlib_zfill
212 : :
213 : : width: Py_ssize_t
214 : : /
215 : :
216 : : Pad a numeric string with zeros on the left, to fill a field of the given width.
217 : :
218 : : The original string is never truncated.
219 : : [clinic start generated code]*/
220 : :
221 : : static PyObject *
222 : 34 : stringlib_zfill_impl(PyObject *self, Py_ssize_t width)
223 : : /*[clinic end generated code: output=0b3c684a7f1b2319 input=2da6d7b8e9bcb19a]*/
224 : : {
225 : : Py_ssize_t fill;
226 : : PyObject *s;
227 : : char *p;
228 : :
229 [ + + ]: 34 : if (STRINGLIB_LEN(self) >= width) {
230 : 22 : return return_self(self);
231 : : }
232 : :
233 : 12 : fill = width - STRINGLIB_LEN(self);
234 : :
235 : 12 : s = pad(self, fill, 0, '0');
236 : :
237 [ - + ]: 12 : if (s == NULL)
238 : 0 : return NULL;
239 : :
240 : 12 : p = STRINGLIB_STR(s);
241 [ + + + + ]: 12 : if (p[fill] == '+' || p[fill] == '-') {
242 : : /* move sign to beginning of string */
243 : 6 : p[0] = p[fill];
244 : 6 : p[fill] = '0';
245 : : }
246 : :
247 : 12 : return s;
248 : : }
249 : :
250 : :
251 : : /* find and count characters and substrings */
252 : :
253 : : #define findchar(target, target_len, c) \
254 : : ((char *)memchr((const void *)(target), c, target_len))
255 : :
256 : :
257 : : static Py_ssize_t
258 : 1439 : countchar(const char *target, Py_ssize_t target_len, char c,
259 : : Py_ssize_t maxcount)
260 : : {
261 : 1439 : Py_ssize_t count = 0;
262 : 1439 : const char *start = target;
263 : 1439 : const char *end = target + target_len;
264 : :
265 [ + + ]: 7031 : while ((start = findchar(start, end - start, c)) != NULL) {
266 : 5612 : count++;
267 [ + + ]: 5612 : if (count >= maxcount)
268 : 20 : break;
269 : 5592 : start += 1;
270 : : }
271 : 1439 : return count;
272 : : }
273 : :
274 : :
275 : : /* Algorithms for different cases of string replacement */
276 : :
277 : : /* len(self)>=1, from="", len(to)>=1, maxcount>=1 */
278 : : static PyObject *
279 : 28 : stringlib_replace_interleave(PyObject *self,
280 : : const char *to_s, Py_ssize_t to_len,
281 : : Py_ssize_t maxcount)
282 : : {
283 : : const char *self_s;
284 : : char *result_s;
285 : : Py_ssize_t self_len, result_len;
286 : : Py_ssize_t count, i;
287 : : PyObject *result;
288 : :
289 : 28 : self_len = STRINGLIB_LEN(self);
290 : :
291 : : /* 1 at the end plus 1 after every character;
292 : : count = min(maxcount, self_len + 1) */
293 [ + + ]: 28 : if (maxcount <= self_len) {
294 : 6 : count = maxcount;
295 : : }
296 : : else {
297 : : /* Can't overflow: self_len + 1 <= maxcount <= PY_SSIZE_T_MAX. */
298 : 22 : count = self_len + 1;
299 : : }
300 : :
301 : : /* Check for overflow */
302 : : /* result_len = count * to_len + self_len; */
303 : : assert(count > 0);
304 [ - + ]: 28 : if (to_len > (PY_SSIZE_T_MAX - self_len) / count) {
305 : 0 : PyErr_SetString(PyExc_OverflowError,
306 : : "replace bytes is too long");
307 : 0 : return NULL;
308 : : }
309 : 28 : result_len = count * to_len + self_len;
310 : 28 : result = STRINGLIB_NEW(NULL, result_len);
311 [ - + ]: 28 : if (result == NULL) {
312 : 0 : return NULL;
313 : : }
314 : :
315 : 28 : self_s = STRINGLIB_STR(self);
316 : 28 : result_s = STRINGLIB_STR(result);
317 : :
318 [ + + ]: 28 : if (to_len > 1) {
319 : : /* Lay the first one down (guaranteed this will occur) */
320 : 18 : memcpy(result_s, to_s, to_len);
321 : 18 : result_s += to_len;
322 : 18 : count -= 1;
323 : :
324 [ + + ]: 44 : for (i = 0; i < count; i++) {
325 : 26 : *result_s++ = *self_s++;
326 : 26 : memcpy(result_s, to_s, to_len);
327 : 26 : result_s += to_len;
328 : : }
329 : : }
330 : : else {
331 : 10 : result_s[0] = to_s[0];
332 : 10 : result_s += to_len;
333 : 10 : count -= 1;
334 [ + + ]: 22 : for (i = 0; i < count; i++) {
335 : 12 : *result_s++ = *self_s++;
336 : 12 : result_s[0] = to_s[0];
337 : 12 : result_s += to_len;
338 : : }
339 : : }
340 : :
341 : : /* Copy the rest of the original string */
342 : 28 : memcpy(result_s, self_s, self_len - i);
343 : :
344 : 28 : return result;
345 : : }
346 : :
347 : : /* Special case for deleting a single character */
348 : : /* len(self)>=1, len(from)==1, to="", maxcount>=1 */
349 : : static PyObject *
350 : 1254 : stringlib_replace_delete_single_character(PyObject *self,
351 : : char from_c, Py_ssize_t maxcount)
352 : : {
353 : : const char *self_s, *start, *next, *end;
354 : : char *result_s;
355 : : Py_ssize_t self_len, result_len;
356 : : Py_ssize_t count;
357 : : PyObject *result;
358 : :
359 : 1254 : self_len = STRINGLIB_LEN(self);
360 : 1254 : self_s = STRINGLIB_STR(self);
361 : :
362 : 1254 : count = countchar(self_s, self_len, from_c, maxcount);
363 [ + + ]: 1254 : if (count == 0) {
364 : 45 : return return_self(self);
365 : : }
366 : :
367 : 1209 : result_len = self_len - count; /* from_len == 1 */
368 : : assert(result_len>=0);
369 : :
370 : 1209 : result = STRINGLIB_NEW(NULL, result_len);
371 [ - + ]: 1209 : if (result == NULL) {
372 : 0 : return NULL;
373 : : }
374 : 1209 : result_s = STRINGLIB_STR(result);
375 : :
376 : 1209 : start = self_s;
377 : 1209 : end = self_s + self_len;
378 [ + + ]: 6610 : while (count-- > 0) {
379 : 5401 : next = findchar(start, end - start, from_c);
380 [ - + ]: 5401 : if (next == NULL)
381 : 0 : break;
382 : 5401 : memcpy(result_s, start, next - start);
383 : 5401 : result_s += (next - start);
384 : 5401 : start = next + 1;
385 : : }
386 : 1209 : memcpy(result_s, start, end - start);
387 : :
388 : 1209 : return result;
389 : : }
390 : :
391 : : /* len(self)>=1, len(from)>=2, to="", maxcount>=1 */
392 : :
393 : : static PyObject *
394 : 85429 : stringlib_replace_delete_substring(PyObject *self,
395 : : const char *from_s, Py_ssize_t from_len,
396 : : Py_ssize_t maxcount)
397 : : {
398 : : const char *self_s, *start, *next, *end;
399 : : char *result_s;
400 : : Py_ssize_t self_len, result_len;
401 : : Py_ssize_t count, offset;
402 : : PyObject *result;
403 : :
404 : 85429 : self_len = STRINGLIB_LEN(self);
405 : 85429 : self_s = STRINGLIB_STR(self);
406 : :
407 : 85429 : count = stringlib_count(self_s, self_len,
408 : : from_s, from_len,
409 : : maxcount);
410 : :
411 [ + + ]: 85429 : if (count == 0) {
412 : : /* no matches */
413 : 78671 : return return_self(self);
414 : : }
415 : :
416 : 6758 : result_len = self_len - (count * from_len);
417 : : assert (result_len>=0);
418 : :
419 : 6758 : result = STRINGLIB_NEW(NULL, result_len);
420 [ - + ]: 6758 : if (result == NULL) {
421 : 0 : return NULL;
422 : : }
423 : 6758 : result_s = STRINGLIB_STR(result);
424 : :
425 : 6758 : start = self_s;
426 : 6758 : end = self_s + self_len;
427 [ + + ]: 14298 : while (count-- > 0) {
428 : 7540 : offset = stringlib_find(start, end - start,
429 : : from_s, from_len,
430 : : 0);
431 [ - + ]: 7540 : if (offset == -1)
432 : 0 : break;
433 : 7540 : next = start + offset;
434 : :
435 : 7540 : memcpy(result_s, start, next - start);
436 : :
437 : 7540 : result_s += (next - start);
438 : 7540 : start = next + from_len;
439 : : }
440 : 6758 : memcpy(result_s, start, end - start);
441 : 6758 : return result;
442 : : }
443 : :
444 : : /* len(self)>=1, len(from)==len(to)==1, maxcount>=1 */
445 : : static PyObject *
446 : 38227 : stringlib_replace_single_character_in_place(PyObject *self,
447 : : char from_c, char to_c,
448 : : Py_ssize_t maxcount)
449 : : {
450 : : const char *self_s, *end;
451 : : char *result_s, *start, *next;
452 : : Py_ssize_t self_len;
453 : : PyObject *result;
454 : :
455 : : /* The result string will be the same size */
456 : 38227 : self_s = STRINGLIB_STR(self);
457 : 38227 : self_len = STRINGLIB_LEN(self);
458 : :
459 : 38227 : next = findchar(self_s, self_len, from_c);
460 : :
461 [ + + ]: 38227 : if (next == NULL) {
462 : : /* No matches; return the original bytes */
463 : 33715 : return return_self(self);
464 : : }
465 : :
466 : : /* Need to make a new bytes */
467 : 4512 : result = STRINGLIB_NEW(NULL, self_len);
468 [ - + ]: 4512 : if (result == NULL) {
469 : 0 : return NULL;
470 : : }
471 : 4512 : result_s = STRINGLIB_STR(result);
472 : 4512 : memcpy(result_s, self_s, self_len);
473 : :
474 : : /* change everything in-place, starting with this one */
475 : 4512 : start = result_s + (next - self_s);
476 : 4512 : *start = to_c;
477 : 4512 : start++;
478 : 4512 : end = result_s + self_len;
479 : :
480 [ + + ]: 36954 : while (--maxcount > 0) {
481 : 36942 : next = findchar(start, end - start, from_c);
482 [ + + ]: 36942 : if (next == NULL)
483 : 4500 : break;
484 : 32442 : *next = to_c;
485 : 32442 : start = next + 1;
486 : : }
487 : :
488 : 4512 : return result;
489 : : }
490 : :
491 : : /* len(self)>=1, len(from)==len(to)>=2, maxcount>=1 */
492 : : static PyObject *
493 : 41 : stringlib_replace_substring_in_place(PyObject *self,
494 : : const char *from_s, Py_ssize_t from_len,
495 : : const char *to_s, Py_ssize_t to_len,
496 : : Py_ssize_t maxcount)
497 : : {
498 : : const char *self_s, *end;
499 : : char *result_s, *start;
500 : : Py_ssize_t self_len, offset;
501 : : PyObject *result;
502 : :
503 : : /* The result bytes will be the same size */
504 : :
505 : 41 : self_s = STRINGLIB_STR(self);
506 : 41 : self_len = STRINGLIB_LEN(self);
507 : :
508 : 41 : offset = stringlib_find(self_s, self_len,
509 : : from_s, from_len,
510 : : 0);
511 [ + + ]: 41 : if (offset == -1) {
512 : : /* No matches; return the original bytes */
513 : 6 : return return_self(self);
514 : : }
515 : :
516 : : /* Need to make a new bytes */
517 : 35 : result = STRINGLIB_NEW(NULL, self_len);
518 [ - + ]: 35 : if (result == NULL) {
519 : 0 : return NULL;
520 : : }
521 : 35 : result_s = STRINGLIB_STR(result);
522 : 35 : memcpy(result_s, self_s, self_len);
523 : :
524 : : /* change everything in-place, starting with this one */
525 : 35 : start = result_s + offset;
526 : 35 : memcpy(start, to_s, from_len);
527 : 35 : start += from_len;
528 : 35 : end = result_s + self_len;
529 : :
530 [ + + ]: 77 : while ( --maxcount > 0) {
531 : 71 : offset = stringlib_find(start, end - start,
532 : : from_s, from_len,
533 : : 0);
534 [ + + ]: 71 : if (offset == -1)
535 : 29 : break;
536 : 42 : memcpy(start + offset, to_s, from_len);
537 : 42 : start += offset + from_len;
538 : : }
539 : :
540 : 35 : return result;
541 : : }
542 : :
543 : : /* len(self)>=1, len(from)==1, len(to)>=2, maxcount>=1 */
544 : : static PyObject *
545 : 185 : stringlib_replace_single_character(PyObject *self,
546 : : char from_c,
547 : : const char *to_s, Py_ssize_t to_len,
548 : : Py_ssize_t maxcount)
549 : : {
550 : : const char *self_s, *start, *next, *end;
551 : : char *result_s;
552 : : Py_ssize_t self_len, result_len;
553 : : Py_ssize_t count;
554 : : PyObject *result;
555 : :
556 : 185 : self_s = STRINGLIB_STR(self);
557 : 185 : self_len = STRINGLIB_LEN(self);
558 : :
559 : 185 : count = countchar(self_s, self_len, from_c, maxcount);
560 [ + + ]: 185 : if (count == 0) {
561 : : /* no matches, return unchanged */
562 : 5 : return return_self(self);
563 : : }
564 : :
565 : : /* use the difference between current and new, hence the "-1" */
566 : : /* result_len = self_len + count * (to_len-1) */
567 : : assert(count > 0);
568 [ - + ]: 180 : if (to_len - 1 > (PY_SSIZE_T_MAX - self_len) / count) {
569 : 0 : PyErr_SetString(PyExc_OverflowError, "replace bytes is too long");
570 : 0 : return NULL;
571 : : }
572 : 180 : result_len = self_len + count * (to_len - 1);
573 : :
574 : 180 : result = STRINGLIB_NEW(NULL, result_len);
575 [ - + ]: 180 : if (result == NULL) {
576 : 0 : return NULL;
577 : : }
578 : 180 : result_s = STRINGLIB_STR(result);
579 : :
580 : 180 : start = self_s;
581 : 180 : end = self_s + self_len;
582 [ + + ]: 391 : while (count-- > 0) {
583 : 211 : next = findchar(start, end - start, from_c);
584 [ - + ]: 211 : if (next == NULL)
585 : 0 : break;
586 : :
587 [ + + ]: 211 : if (next == start) {
588 : : /* replace with the 'to' */
589 : 28 : memcpy(result_s, to_s, to_len);
590 : 28 : result_s += to_len;
591 : 28 : start += 1;
592 : : } else {
593 : : /* copy the unchanged old then the 'to' */
594 : 183 : memcpy(result_s, start, next - start);
595 : 183 : result_s += (next - start);
596 : 183 : memcpy(result_s, to_s, to_len);
597 : 183 : result_s += to_len;
598 : 183 : start = next + 1;
599 : : }
600 : : }
601 : : /* Copy the remainder of the remaining bytes */
602 : 180 : memcpy(result_s, start, end - start);
603 : :
604 : 180 : return result;
605 : : }
606 : :
607 : : /* len(self)>=1, len(from)>=2, len(to)>=2, maxcount>=1 */
608 : : static PyObject *
609 : 4175 : stringlib_replace_substring(PyObject *self,
610 : : const char *from_s, Py_ssize_t from_len,
611 : : const char *to_s, Py_ssize_t to_len,
612 : : Py_ssize_t maxcount)
613 : : {
614 : : const char *self_s, *start, *next, *end;
615 : : char *result_s;
616 : : Py_ssize_t self_len, result_len;
617 : : Py_ssize_t count, offset;
618 : : PyObject *result;
619 : :
620 : 4175 : self_s = STRINGLIB_STR(self);
621 : 4175 : self_len = STRINGLIB_LEN(self);
622 : :
623 : 4175 : count = stringlib_count(self_s, self_len,
624 : : from_s, from_len,
625 : : maxcount);
626 : :
627 [ + + ]: 4175 : if (count == 0) {
628 : : /* no matches, return unchanged */
629 : 4125 : return return_self(self);
630 : : }
631 : :
632 : : /* Check for overflow */
633 : : /* result_len = self_len + count * (to_len-from_len) */
634 : : assert(count > 0);
635 [ - + ]: 50 : if (to_len - from_len > (PY_SSIZE_T_MAX - self_len) / count) {
636 : 0 : PyErr_SetString(PyExc_OverflowError, "replace bytes is too long");
637 : 0 : return NULL;
638 : : }
639 : 50 : result_len = self_len + count * (to_len - from_len);
640 : :
641 : 50 : result = STRINGLIB_NEW(NULL, result_len);
642 [ - + ]: 50 : if (result == NULL) {
643 : 0 : return NULL;
644 : : }
645 : 50 : result_s = STRINGLIB_STR(result);
646 : :
647 : 50 : start = self_s;
648 : 50 : end = self_s + self_len;
649 [ + + ]: 333 : while (count-- > 0) {
650 : 283 : offset = stringlib_find(start, end - start,
651 : : from_s, from_len,
652 : : 0);
653 [ - + ]: 283 : if (offset == -1)
654 : 0 : break;
655 : 283 : next = start + offset;
656 [ + + ]: 283 : if (next == start) {
657 : : /* replace with the 'to' */
658 : 65 : memcpy(result_s, to_s, to_len);
659 : 65 : result_s += to_len;
660 : 65 : start += from_len;
661 : : } else {
662 : : /* copy the unchanged old then the 'to' */
663 : 218 : memcpy(result_s, start, next - start);
664 : 218 : result_s += (next - start);
665 : 218 : memcpy(result_s, to_s, to_len);
666 : 218 : result_s += to_len;
667 : 218 : start = next + from_len;
668 : : }
669 : : }
670 : : /* Copy the remainder of the remaining bytes */
671 : 50 : memcpy(result_s, start, end - start);
672 : :
673 : 50 : return result;
674 : : }
675 : :
676 : :
677 : : static PyObject *
678 : 172911 : stringlib_replace(PyObject *self,
679 : : const char *from_s, Py_ssize_t from_len,
680 : : const char *to_s, Py_ssize_t to_len,
681 : : Py_ssize_t maxcount)
682 : : {
683 [ + + ]: 172911 : if (STRINGLIB_LEN(self) < from_len) {
684 : : /* nothing to do; return the original bytes */
685 : 43521 : return return_self(self);
686 : : }
687 [ + + ]: 129390 : if (maxcount < 0) {
688 : 129257 : maxcount = PY_SSIZE_T_MAX;
689 [ + + ]: 133 : } else if (maxcount == 0) {
690 : : /* nothing to do; return the original bytes */
691 : 34 : return return_self(self);
692 : : }
693 : :
694 : : /* Handle zero-length special cases */
695 [ + + ]: 129356 : if (from_len == 0) {
696 [ + + ]: 45 : if (to_len == 0) {
697 : : /* nothing to do; return the original bytes */
698 : 17 : return return_self(self);
699 : : }
700 : : /* insert the 'to' bytes everywhere. */
701 : : /* >>> b"Python".replace(b"", b".") */
702 : : /* b'.P.y.t.h.o.n.' */
703 : 28 : return stringlib_replace_interleave(self, to_s, to_len, maxcount);
704 : : }
705 : :
706 [ + + ]: 129311 : if (to_len == 0) {
707 : : /* delete all occurrences of 'from' bytes */
708 [ + + ]: 86683 : if (from_len == 1) {
709 : 1254 : return stringlib_replace_delete_single_character(
710 : 1254 : self, from_s[0], maxcount);
711 : : } else {
712 : 85429 : return stringlib_replace_delete_substring(
713 : : self, from_s, from_len, maxcount);
714 : : }
715 : : }
716 : :
717 : : /* Handle special case where both bytes have the same length */
718 : :
719 [ + + ]: 42628 : if (from_len == to_len) {
720 [ + + ]: 38268 : if (from_len == 1) {
721 : 38227 : return stringlib_replace_single_character_in_place(
722 : 38227 : self, from_s[0], to_s[0], maxcount);
723 : : } else {
724 : 41 : return stringlib_replace_substring_in_place(
725 : : self, from_s, from_len, to_s, to_len, maxcount);
726 : : }
727 : : }
728 : :
729 : : /* Otherwise use the more generic algorithms */
730 [ + + ]: 4360 : if (from_len == 1) {
731 : 185 : return stringlib_replace_single_character(
732 : 185 : self, from_s[0], to_s, to_len, maxcount);
733 : : } else {
734 : : /* len('from')>=2, len('to')>=1 */
735 : 4175 : return stringlib_replace_substring(
736 : : self, from_s, from_len, to_s, to_len, maxcount);
737 : : }
738 : : }
739 : :
740 : : #undef findchar
|