LCOV - code coverage report
Current view: top level - Objects/stringlib - transmogrify.h (source / functions) Hit Total Coverage
Test: CPython lcov report Lines: 282 311 90.7 %
Date: 2022-07-07 18:19:46 Functions: 16 16 100.0 %

          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      160398 : return_self(PyObject *self)
      17             : {
      18             : #if !STRINGLIB_MUTABLE
      19       99413 :     if (STRINGLIB_CHECK_EXACT(self)) {
      20       99367 :         Py_INCREF(self);
      21       99367 :         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          28 :     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        1209 :     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        6758 :     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       38433 : 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       38433 :     self_s = STRINGLIB_STR(self);
     457       38433 :     self_len = STRINGLIB_LEN(self);
     458             : 
     459       38433 :     next = findchar(self_s, self_len, from_c);
     460             : 
     461       38433 :     if (next == NULL) {
     462             :         /* No matches; return the original bytes */
     463       33919 :         return return_self(self);
     464             :     }
     465             : 
     466             :     /* Need to make a new bytes */
     467        4514 :     result = STRINGLIB_NEW(NULL, self_len);
     468        4514 :     if (result == NULL) {
     469           0 :         return NULL;
     470             :     }
     471        4514 :     result_s = STRINGLIB_STR(result);
     472        4514 :     memcpy(result_s, self_s, self_len);
     473             : 
     474             :     /* change everything in-place, starting with this one */
     475        4514 :     start =  result_s + (next - self_s);
     476        4514 :     *start = to_c;
     477        4514 :     start++;
     478        4514 :     end = result_s + self_len;
     479             : 
     480       36808 :     while (--maxcount > 0) {
     481       36796 :         next = findchar(start, end - start, from_c);
     482       36796 :         if (next == NULL)
     483        4502 :             break;
     484       32294 :         *next = to_c;
     485       32294 :         start = next + 1;
     486             :     }
     487             : 
     488        4514 :     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         180 :     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        4177 : 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        4177 :     self_s = STRINGLIB_STR(self);
     621        4177 :     self_len = STRINGLIB_LEN(self);
     622             : 
     623        4177 :     count = stringlib_count(self_s, self_len,
     624             :                             from_s, from_len,
     625             :                             maxcount);
     626             : 
     627        4177 :     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          52 :     assert(count > 0);
     635          52 :     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          52 :     result_len = self_len + count * (to_len - from_len);
     640             : 
     641          52 :     result = STRINGLIB_NEW(NULL, result_len);
     642          52 :     if (result == NULL) {
     643           0 :         return NULL;
     644             :     }
     645          52 :     result_s = STRINGLIB_STR(result);
     646             : 
     647          52 :     start = self_s;
     648          52 :     end = self_s + self_len;
     649         337 :     while (count-- > 0) {
     650         285 :         offset = stringlib_find(start, end - start,
     651             :                                 from_s, from_len,
     652             :                                 0);
     653         285 :         if (offset == -1)
     654           0 :             break;
     655         285 :         next = start + offset;
     656         285 :         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         220 :             memcpy(result_s, start, next - start);
     664         220 :             result_s += (next - start);
     665         220 :             memcpy(result_s, to_s, to_len);
     666         220 :             result_s += to_len;
     667         220 :             start = next + from_len;
     668             :         }
     669             :     }
     670             :     /* Copy the remainder of the remaining bytes */
     671          52 :     memcpy(result_s, start, end - start);
     672             : 
     673          52 :     return result;
     674             : }
     675             : 
     676             : 
     677             : static PyObject *
     678      173119 : 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      173119 :     if (STRINGLIB_LEN(self) < from_len) {
     684             :         /* nothing to do; return the original bytes */
     685       43521 :         return return_self(self);
     686             :     }
     687      129598 :     if (maxcount < 0) {
     688      129465 :         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      129564 :     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      129519 :     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       42836 :     if (from_len == to_len) {
     720       38474 :         if (from_len == 1) {
     721       38433 :             return stringlib_replace_single_character_in_place(
     722       38433 :                 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        4362 :     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        4177 :         return stringlib_replace_substring(
     736             :             self, from_s, from_len, to_s, to_len, maxcount);
     737             :     }
     738             : }
     739             : 
     740             : #undef findchar

Generated by: LCOV version 1.14