(root)/
Python-3.12.0/
Objects/
bytes_methods.c
       1  #define PY_SSIZE_T_CLEAN
       2  #include "Python.h"
       3  #include "pycore_abstract.h"   // _PyIndex_Check()
       4  #include "pycore_bytes_methods.h"
       5  
       6  PyDoc_STRVAR_shared(_Py_isspace__doc__,
       7  "B.isspace() -> bool\n\
       8  \n\
       9  Return True if all characters in B are whitespace\n\
      10  and there is at least one character in B, False otherwise.");
      11  
      12  PyObject*
      13  _Py_bytes_isspace(const char *cptr, Py_ssize_t len)
      14  {
      15      const unsigned char *p
      16          = (const unsigned char *) cptr;
      17      const unsigned char *e;
      18  
      19      /* Shortcut for single character strings */
      20      if (len == 1 && Py_ISSPACE(*p))
      21          Py_RETURN_TRUE;
      22  
      23      /* Special case for empty strings */
      24      if (len == 0)
      25          Py_RETURN_FALSE;
      26  
      27      e = p + len;
      28      for (; p < e; p++) {
      29          if (!Py_ISSPACE(*p))
      30              Py_RETURN_FALSE;
      31      }
      32      Py_RETURN_TRUE;
      33  }
      34  
      35  
      36  PyDoc_STRVAR_shared(_Py_isalpha__doc__,
      37  "B.isalpha() -> bool\n\
      38  \n\
      39  Return True if all characters in B are alphabetic\n\
      40  and there is at least one character in B, False otherwise.");
      41  
      42  PyObject*
      43  _Py_bytes_isalpha(const char *cptr, Py_ssize_t len)
      44  {
      45      const unsigned char *p
      46          = (const unsigned char *) cptr;
      47      const unsigned char *e;
      48  
      49      /* Shortcut for single character strings */
      50      if (len == 1 && Py_ISALPHA(*p))
      51          Py_RETURN_TRUE;
      52  
      53      /* Special case for empty strings */
      54      if (len == 0)
      55          Py_RETURN_FALSE;
      56  
      57      e = p + len;
      58      for (; p < e; p++) {
      59          if (!Py_ISALPHA(*p))
      60              Py_RETURN_FALSE;
      61      }
      62      Py_RETURN_TRUE;
      63  }
      64  
      65  
      66  PyDoc_STRVAR_shared(_Py_isalnum__doc__,
      67  "B.isalnum() -> bool\n\
      68  \n\
      69  Return True if all characters in B are alphanumeric\n\
      70  and there is at least one character in B, False otherwise.");
      71  
      72  PyObject*
      73  _Py_bytes_isalnum(const char *cptr, Py_ssize_t len)
      74  {
      75      const unsigned char *p
      76          = (const unsigned char *) cptr;
      77      const unsigned char *e;
      78  
      79      /* Shortcut for single character strings */
      80      if (len == 1 && Py_ISALNUM(*p))
      81          Py_RETURN_TRUE;
      82  
      83      /* Special case for empty strings */
      84      if (len == 0)
      85          Py_RETURN_FALSE;
      86  
      87      e = p + len;
      88      for (; p < e; p++) {
      89          if (!Py_ISALNUM(*p))
      90              Py_RETURN_FALSE;
      91      }
      92      Py_RETURN_TRUE;
      93  }
      94  
      95  
      96  PyDoc_STRVAR_shared(_Py_isascii__doc__,
      97  "B.isascii() -> bool\n\
      98  \n\
      99  Return True if B is empty or all characters in B are ASCII,\n\
     100  False otherwise.");
     101  
     102  // Optimization is copied from ascii_decode in unicodeobject.c
     103  /* Mask to quickly check whether a C 'size_t' contains a
     104     non-ASCII, UTF8-encoded char. */
     105  #if (SIZEOF_SIZE_T == 8)
     106  # define ASCII_CHAR_MASK 0x8080808080808080ULL
     107  #elif (SIZEOF_SIZE_T == 4)
     108  # define ASCII_CHAR_MASK 0x80808080U
     109  #else
     110  # error C 'size_t' size should be either 4 or 8!
     111  #endif
     112  
     113  PyObject*
     114  _Py_bytes_isascii(const char *cptr, Py_ssize_t len)
     115  {
     116      const char *p = cptr;
     117      const char *end = p + len;
     118  
     119      while (p < end) {
     120          /* Fast path, see in STRINGLIB(utf8_decode) in stringlib/codecs.h
     121             for an explanation. */
     122          if (_Py_IS_ALIGNED(p, ALIGNOF_SIZE_T)) {
     123              /* Help allocation */
     124              const char *_p = p;
     125              while (_p + SIZEOF_SIZE_T <= end) {
     126                  size_t value = *(const size_t *) _p;
     127                  if (value & ASCII_CHAR_MASK) {
     128                      Py_RETURN_FALSE;
     129                  }
     130                  _p += SIZEOF_SIZE_T;
     131              }
     132              p = _p;
     133              if (_p == end)
     134                  break;
     135          }
     136          if ((unsigned char)*p & 0x80) {
     137              Py_RETURN_FALSE;
     138          }
     139          p++;
     140      }
     141      Py_RETURN_TRUE;
     142  }
     143  
     144  #undef ASCII_CHAR_MASK
     145  
     146  
     147  PyDoc_STRVAR_shared(_Py_isdigit__doc__,
     148  "B.isdigit() -> bool\n\
     149  \n\
     150  Return True if all characters in B are digits\n\
     151  and there is at least one character in B, False otherwise.");
     152  
     153  PyObject*
     154  _Py_bytes_isdigit(const char *cptr, Py_ssize_t len)
     155  {
     156      const unsigned char *p
     157          = (const unsigned char *) cptr;
     158      const unsigned char *e;
     159  
     160      /* Shortcut for single character strings */
     161      if (len == 1 && Py_ISDIGIT(*p))
     162          Py_RETURN_TRUE;
     163  
     164      /* Special case for empty strings */
     165      if (len == 0)
     166          Py_RETURN_FALSE;
     167  
     168      e = p + len;
     169      for (; p < e; p++) {
     170          if (!Py_ISDIGIT(*p))
     171              Py_RETURN_FALSE;
     172      }
     173      Py_RETURN_TRUE;
     174  }
     175  
     176  
     177  PyDoc_STRVAR_shared(_Py_islower__doc__,
     178  "B.islower() -> bool\n\
     179  \n\
     180  Return True if all cased characters in B are lowercase and there is\n\
     181  at least one cased character in B, False otherwise.");
     182  
     183  PyObject*
     184  _Py_bytes_islower(const char *cptr, Py_ssize_t len)
     185  {
     186      const unsigned char *p
     187          = (const unsigned char *) cptr;
     188      const unsigned char *e;
     189      int cased;
     190  
     191      /* Shortcut for single character strings */
     192      if (len == 1)
     193          return PyBool_FromLong(Py_ISLOWER(*p));
     194  
     195      /* Special case for empty strings */
     196      if (len == 0)
     197          Py_RETURN_FALSE;
     198  
     199      e = p + len;
     200      cased = 0;
     201      for (; p < e; p++) {
     202          if (Py_ISUPPER(*p))
     203              Py_RETURN_FALSE;
     204          else if (!cased && Py_ISLOWER(*p))
     205              cased = 1;
     206      }
     207      return PyBool_FromLong(cased);
     208  }
     209  
     210  
     211  PyDoc_STRVAR_shared(_Py_isupper__doc__,
     212  "B.isupper() -> bool\n\
     213  \n\
     214  Return True if all cased characters in B are uppercase and there is\n\
     215  at least one cased character in B, False otherwise.");
     216  
     217  PyObject*
     218  _Py_bytes_isupper(const char *cptr, Py_ssize_t len)
     219  {
     220      const unsigned char *p
     221          = (const unsigned char *) cptr;
     222      const unsigned char *e;
     223      int cased;
     224  
     225      /* Shortcut for single character strings */
     226      if (len == 1)
     227          return PyBool_FromLong(Py_ISUPPER(*p));
     228  
     229      /* Special case for empty strings */
     230      if (len == 0)
     231          Py_RETURN_FALSE;
     232  
     233      e = p + len;
     234      cased = 0;
     235      for (; p < e; p++) {
     236          if (Py_ISLOWER(*p))
     237              Py_RETURN_FALSE;
     238          else if (!cased && Py_ISUPPER(*p))
     239              cased = 1;
     240      }
     241      return PyBool_FromLong(cased);
     242  }
     243  
     244  
     245  PyDoc_STRVAR_shared(_Py_istitle__doc__,
     246  "B.istitle() -> bool\n\
     247  \n\
     248  Return True if B is a titlecased string and there is at least one\n\
     249  character in B, i.e. uppercase characters may only follow uncased\n\
     250  characters and lowercase characters only cased ones. Return False\n\
     251  otherwise.");
     252  
     253  PyObject*
     254  _Py_bytes_istitle(const char *cptr, Py_ssize_t len)
     255  {
     256      const unsigned char *p
     257          = (const unsigned char *) cptr;
     258      const unsigned char *e;
     259      int cased, previous_is_cased;
     260  
     261      if (len == 1) {
     262          if (Py_ISUPPER(*p)) {
     263              Py_RETURN_TRUE;
     264          }
     265          Py_RETURN_FALSE;
     266      }
     267  
     268      /* Special case for empty strings */
     269      if (len == 0)
     270          Py_RETURN_FALSE;
     271  
     272      e = p + len;
     273      cased = 0;
     274      previous_is_cased = 0;
     275      for (; p < e; p++) {
     276          const unsigned char ch = *p;
     277  
     278          if (Py_ISUPPER(ch)) {
     279              if (previous_is_cased)
     280                  Py_RETURN_FALSE;
     281              previous_is_cased = 1;
     282              cased = 1;
     283          }
     284          else if (Py_ISLOWER(ch)) {
     285              if (!previous_is_cased)
     286                  Py_RETURN_FALSE;
     287              previous_is_cased = 1;
     288              cased = 1;
     289          }
     290          else
     291              previous_is_cased = 0;
     292      }
     293      return PyBool_FromLong(cased);
     294  }
     295  
     296  
     297  PyDoc_STRVAR_shared(_Py_lower__doc__,
     298  "B.lower() -> copy of B\n\
     299  \n\
     300  Return a copy of B with all ASCII characters converted to lowercase.");
     301  
     302  void
     303  _Py_bytes_lower(char *result, const char *cptr, Py_ssize_t len)
     304  {
     305      Py_ssize_t i;
     306  
     307      for (i = 0; i < len; i++) {
     308          result[i] = Py_TOLOWER((unsigned char) cptr[i]);
     309      }
     310  }
     311  
     312  
     313  PyDoc_STRVAR_shared(_Py_upper__doc__,
     314  "B.upper() -> copy of B\n\
     315  \n\
     316  Return a copy of B with all ASCII characters converted to uppercase.");
     317  
     318  void
     319  _Py_bytes_upper(char *result, const char *cptr, Py_ssize_t len)
     320  {
     321      Py_ssize_t i;
     322  
     323      for (i = 0; i < len; i++) {
     324          result[i] = Py_TOUPPER((unsigned char) cptr[i]);
     325      }
     326  }
     327  
     328  
     329  PyDoc_STRVAR_shared(_Py_title__doc__,
     330  "B.title() -> copy of B\n\
     331  \n\
     332  Return a titlecased version of B, i.e. ASCII words start with uppercase\n\
     333  characters, all remaining cased characters have lowercase.");
     334  
     335  void
     336  _Py_bytes_title(char *result, const char *s, Py_ssize_t len)
     337  {
     338      Py_ssize_t i;
     339      int previous_is_cased = 0;
     340  
     341      for (i = 0; i < len; i++) {
     342          int c = Py_CHARMASK(*s++);
     343          if (Py_ISLOWER(c)) {
     344              if (!previous_is_cased)
     345                  c = Py_TOUPPER(c);
     346              previous_is_cased = 1;
     347          } else if (Py_ISUPPER(c)) {
     348              if (previous_is_cased)
     349                  c = Py_TOLOWER(c);
     350              previous_is_cased = 1;
     351          } else
     352              previous_is_cased = 0;
     353          *result++ = c;
     354      }
     355  }
     356  
     357  
     358  PyDoc_STRVAR_shared(_Py_capitalize__doc__,
     359  "B.capitalize() -> copy of B\n\
     360  \n\
     361  Return a copy of B with only its first character capitalized (ASCII)\n\
     362  and the rest lower-cased.");
     363  
     364  void
     365  _Py_bytes_capitalize(char *result, const char *s, Py_ssize_t len)
     366  {
     367      if (len > 0) {
     368          *result = Py_TOUPPER(*s);
     369          _Py_bytes_lower(result + 1, s + 1, len - 1);
     370      }
     371  }
     372  
     373  
     374  PyDoc_STRVAR_shared(_Py_swapcase__doc__,
     375  "B.swapcase() -> copy of B\n\
     376  \n\
     377  Return a copy of B with uppercase ASCII characters converted\n\
     378  to lowercase ASCII and vice versa.");
     379  
     380  void
     381  _Py_bytes_swapcase(char *result, const char *s, Py_ssize_t len)
     382  {
     383      Py_ssize_t i;
     384  
     385      for (i = 0; i < len; i++) {
     386          int c = Py_CHARMASK(*s++);
     387          if (Py_ISLOWER(c)) {
     388              *result = Py_TOUPPER(c);
     389          }
     390          else if (Py_ISUPPER(c)) {
     391              *result = Py_TOLOWER(c);
     392          }
     393          else
     394              *result = c;
     395          result++;
     396      }
     397  }
     398  
     399  
     400  PyDoc_STRVAR_shared(_Py_maketrans__doc__,
     401  "B.maketrans(frm, to) -> translation table\n\
     402  \n\
     403  Return a translation table (a bytes object of length 256) suitable\n\
     404  for use in the bytes or bytearray translate method where each byte\n\
     405  in frm is mapped to the byte at the same position in to.\n\
     406  The bytes objects frm and to must be of the same length.");
     407  
     408  PyObject *
     409  _Py_bytes_maketrans(Py_buffer *frm, Py_buffer *to)
     410  {
     411      PyObject *res = NULL;
     412      Py_ssize_t i;
     413      char *p;
     414  
     415      if (frm->len != to->len) {
     416          PyErr_Format(PyExc_ValueError,
     417                       "maketrans arguments must have same length");
     418          return NULL;
     419      }
     420      res = PyBytes_FromStringAndSize(NULL, 256);
     421      if (!res)
     422          return NULL;
     423      p = PyBytes_AS_STRING(res);
     424      for (i = 0; i < 256; i++)
     425          p[i] = (char) i;
     426      for (i = 0; i < frm->len; i++) {
     427          p[((unsigned char *)frm->buf)[i]] = ((char *)to->buf)[i];
     428      }
     429  
     430      return res;
     431  }
     432  
     433  #define FASTSEARCH fastsearch
     434  #define STRINGLIB(F) stringlib_##F
     435  #define STRINGLIB_CHAR char
     436  #define STRINGLIB_SIZEOF_CHAR 1
     437  #define STRINGLIB_FAST_MEMCHR memchr
     438  
     439  #include "stringlib/fastsearch.h"
     440  #include "stringlib/count.h"
     441  #include "stringlib/find.h"
     442  
     443  /*
     444  Wraps stringlib_parse_args_finds() and additionally checks the first
     445  argument type.
     446  
     447  In case the first argument is a bytes-like object, sets it to subobj,
     448  and doesn't touch the byte parameter.
     449  In case it is an integer in range(0, 256), writes the integer value
     450  to byte, and sets subobj to NULL.
     451  
     452  The other parameters are similar to those of
     453  stringlib_parse_args_finds().
     454  */
     455  
     456  Py_LOCAL_INLINE(int)
     457  parse_args_finds_byte(const char *function_name, PyObject *args,
     458                        PyObject **subobj, char *byte,
     459                        Py_ssize_t *start, Py_ssize_t *end)
     460  {
     461      PyObject *tmp_subobj;
     462      Py_ssize_t ival;
     463  
     464      if(!stringlib_parse_args_finds(function_name, args, &tmp_subobj,
     465                                     start, end))
     466          return 0;
     467  
     468      if (PyObject_CheckBuffer(tmp_subobj)) {
     469          *subobj = tmp_subobj;
     470          return 1;
     471      }
     472  
     473      if (!_PyIndex_Check(tmp_subobj)) {
     474          PyErr_Format(PyExc_TypeError,
     475                       "argument should be integer or bytes-like object, "
     476                       "not '%.200s'",
     477                       Py_TYPE(tmp_subobj)->tp_name);
     478          return 0;
     479      }
     480  
     481      ival = PyNumber_AsSsize_t(tmp_subobj, NULL);
     482      if (ival == -1 && PyErr_Occurred()) {
     483          return 0;
     484      }
     485      if (ival < 0 || ival > 255) {
     486          PyErr_SetString(PyExc_ValueError, "byte must be in range(0, 256)");
     487          return 0;
     488      }
     489  
     490      *subobj = NULL;
     491      *byte = (char)ival;
     492      return 1;
     493  }
     494  
     495  /* helper macro to fixup start/end slice values */
     496  #define ADJUST_INDICES(start, end, len)         \
     497      if (end > len)                          \
     498          end = len;                          \
     499      else if (end < 0) {                     \
     500          end += len;                         \
     501          if (end < 0)                        \
     502          end = 0;                        \
     503      }                                       \
     504      if (start < 0) {                        \
     505          start += len;                       \
     506          if (start < 0)                      \
     507          start = 0;                      \
     508      }
     509  
     510  Py_LOCAL_INLINE(Py_ssize_t)
     511  find_internal(const char *str, Py_ssize_t len,
     512                const char *function_name, PyObject *args, int dir)
     513  {
     514      PyObject *subobj;
     515      char byte;
     516      Py_buffer subbuf;
     517      const char *sub;
     518      Py_ssize_t sub_len;
     519      Py_ssize_t start = 0, end = PY_SSIZE_T_MAX;
     520      Py_ssize_t res;
     521  
     522      if (!parse_args_finds_byte(function_name, args,
     523                                 &subobj, &byte, &start, &end))
     524          return -2;
     525  
     526      if (subobj) {
     527          if (PyObject_GetBuffer(subobj, &subbuf, PyBUF_SIMPLE) != 0)
     528              return -2;
     529  
     530          sub = subbuf.buf;
     531          sub_len = subbuf.len;
     532      }
     533      else {
     534          sub = &byte;
     535          sub_len = 1;
     536      }
     537  
     538      ADJUST_INDICES(start, end, len);
     539      if (end - start < sub_len)
     540          res = -1;
     541      else if (sub_len == 1) {
     542          if (dir > 0)
     543              res = stringlib_find_char(
     544                  str + start, end - start,
     545                  *sub);
     546          else
     547              res = stringlib_rfind_char(
     548                  str + start, end - start,
     549                  *sub);
     550          if (res >= 0)
     551              res += start;
     552      }
     553      else {
     554          if (dir > 0)
     555              res = stringlib_find_slice(
     556                  str, len,
     557                  sub, sub_len, start, end);
     558          else
     559              res = stringlib_rfind_slice(
     560                  str, len,
     561                  sub, sub_len, start, end);
     562      }
     563  
     564      if (subobj)
     565          PyBuffer_Release(&subbuf);
     566  
     567      return res;
     568  }
     569  
     570  PyDoc_STRVAR_shared(_Py_find__doc__,
     571  "B.find(sub[, start[, end]]) -> int\n\
     572  \n\
     573  Return the lowest index in B where subsection sub is found,\n\
     574  such that sub is contained within B[start,end].  Optional\n\
     575  arguments start and end are interpreted as in slice notation.\n\
     576  \n\
     577  Return -1 on failure.");
     578  
     579  PyObject *
     580  _Py_bytes_find(const char *str, Py_ssize_t len, PyObject *args)
     581  {
     582      Py_ssize_t result = find_internal(str, len, "find", args, +1);
     583      if (result == -2)
     584          return NULL;
     585      return PyLong_FromSsize_t(result);
     586  }
     587  
     588  PyDoc_STRVAR_shared(_Py_index__doc__,
     589  "B.index(sub[, start[, end]]) -> int\n\
     590  \n\
     591  Return the lowest index in B where subsection sub is found,\n\
     592  such that sub is contained within B[start,end].  Optional\n\
     593  arguments start and end are interpreted as in slice notation.\n\
     594  \n\
     595  Raises ValueError when the subsection is not found.");
     596  
     597  PyObject *
     598  _Py_bytes_index(const char *str, Py_ssize_t len, PyObject *args)
     599  {
     600      Py_ssize_t result = find_internal(str, len, "index", args, +1);
     601      if (result == -2)
     602          return NULL;
     603      if (result == -1) {
     604          PyErr_SetString(PyExc_ValueError,
     605                          "subsection not found");
     606          return NULL;
     607      }
     608      return PyLong_FromSsize_t(result);
     609  }
     610  
     611  PyDoc_STRVAR_shared(_Py_rfind__doc__,
     612  "B.rfind(sub[, start[, end]]) -> int\n\
     613  \n\
     614  Return the highest index in B where subsection sub is found,\n\
     615  such that sub is contained within B[start,end].  Optional\n\
     616  arguments start and end are interpreted as in slice notation.\n\
     617  \n\
     618  Return -1 on failure.");
     619  
     620  PyObject *
     621  _Py_bytes_rfind(const char *str, Py_ssize_t len, PyObject *args)
     622  {
     623      Py_ssize_t result = find_internal(str, len, "rfind", args, -1);
     624      if (result == -2)
     625          return NULL;
     626      return PyLong_FromSsize_t(result);
     627  }
     628  
     629  PyDoc_STRVAR_shared(_Py_rindex__doc__,
     630  "B.rindex(sub[, start[, end]]) -> int\n\
     631  \n\
     632  Return the highest index in B where subsection sub is found,\n\
     633  such that sub is contained within B[start,end].  Optional\n\
     634  arguments start and end are interpreted as in slice notation.\n\
     635  \n\
     636  Raise ValueError when the subsection is not found.");
     637  
     638  PyObject *
     639  _Py_bytes_rindex(const char *str, Py_ssize_t len, PyObject *args)
     640  {
     641      Py_ssize_t result = find_internal(str, len, "rindex", args, -1);
     642      if (result == -2)
     643          return NULL;
     644      if (result == -1) {
     645          PyErr_SetString(PyExc_ValueError,
     646                          "subsection not found");
     647          return NULL;
     648      }
     649      return PyLong_FromSsize_t(result);
     650  }
     651  
     652  PyDoc_STRVAR_shared(_Py_count__doc__,
     653  "B.count(sub[, start[, end]]) -> int\n\
     654  \n\
     655  Return the number of non-overlapping occurrences of subsection sub in\n\
     656  bytes B[start:end].  Optional arguments start and end are interpreted\n\
     657  as in slice notation.");
     658  
     659  PyObject *
     660  _Py_bytes_count(const char *str, Py_ssize_t len, PyObject *args)
     661  {
     662      PyObject *sub_obj;
     663      const char *sub;
     664      Py_ssize_t sub_len;
     665      char byte;
     666      Py_ssize_t start = 0, end = PY_SSIZE_T_MAX;
     667  
     668      Py_buffer vsub;
     669      PyObject *count_obj;
     670  
     671      if (!parse_args_finds_byte("count", args,
     672                                 &sub_obj, &byte, &start, &end))
     673          return NULL;
     674  
     675      if (sub_obj) {
     676          if (PyObject_GetBuffer(sub_obj, &vsub, PyBUF_SIMPLE) != 0)
     677              return NULL;
     678  
     679          sub = vsub.buf;
     680          sub_len = vsub.len;
     681      }
     682      else {
     683          sub = &byte;
     684          sub_len = 1;
     685      }
     686  
     687      ADJUST_INDICES(start, end, len);
     688  
     689      count_obj = PyLong_FromSsize_t(
     690          stringlib_count(str + start, end - start, sub, sub_len, PY_SSIZE_T_MAX)
     691          );
     692  
     693      if (sub_obj)
     694          PyBuffer_Release(&vsub);
     695  
     696      return count_obj;
     697  }
     698  
     699  int
     700  _Py_bytes_contains(const char *str, Py_ssize_t len, PyObject *arg)
     701  {
     702      Py_ssize_t ival = PyNumber_AsSsize_t(arg, NULL);
     703      if (ival == -1 && PyErr_Occurred()) {
     704          Py_buffer varg;
     705          Py_ssize_t pos;
     706          PyErr_Clear();
     707          if (PyObject_GetBuffer(arg, &varg, PyBUF_SIMPLE) != 0)
     708              return -1;
     709          pos = stringlib_find(str, len,
     710                               varg.buf, varg.len, 0);
     711          PyBuffer_Release(&varg);
     712          return pos >= 0;
     713      }
     714      if (ival < 0 || ival >= 256) {
     715          PyErr_SetString(PyExc_ValueError, "byte must be in range(0, 256)");
     716          return -1;
     717      }
     718  
     719      return memchr(str, (int) ival, len) != NULL;
     720  }
     721  
     722  
     723  /* Matches the end (direction >= 0) or start (direction < 0) of the buffer
     724   * against substr, using the start and end arguments. Returns
     725   * -1 on error, 0 if not found and 1 if found.
     726   */
     727  static int
     728  tailmatch(const char *str, Py_ssize_t len, PyObject *substr,
     729            Py_ssize_t start, Py_ssize_t end, int direction)
     730  {
     731      Py_buffer sub_view = {NULL, NULL};
     732      const char *sub;
     733      Py_ssize_t slen;
     734  
     735      if (PyBytes_Check(substr)) {
     736          sub = PyBytes_AS_STRING(substr);
     737          slen = PyBytes_GET_SIZE(substr);
     738      }
     739      else {
     740          if (PyObject_GetBuffer(substr, &sub_view, PyBUF_SIMPLE) != 0)
     741              return -1;
     742          sub = sub_view.buf;
     743          slen = sub_view.len;
     744      }
     745  
     746      ADJUST_INDICES(start, end, len);
     747  
     748      if (direction < 0) {
     749          /* startswith */
     750          if (start > len - slen)
     751              goto notfound;
     752      } else {
     753          /* endswith */
     754          if (end - start < slen || start > len)
     755              goto notfound;
     756  
     757          if (end - slen > start)
     758              start = end - slen;
     759      }
     760      if (end - start < slen)
     761          goto notfound;
     762      if (memcmp(str + start, sub, slen) != 0)
     763          goto notfound;
     764  
     765      PyBuffer_Release(&sub_view);
     766      return 1;
     767  
     768  notfound:
     769      PyBuffer_Release(&sub_view);
     770      return 0;
     771  }
     772  
     773  static PyObject *
     774  _Py_bytes_tailmatch(const char *str, Py_ssize_t len,
     775                      const char *function_name, PyObject *args,
     776                      int direction)
     777  {
     778      Py_ssize_t start = 0;
     779      Py_ssize_t end = PY_SSIZE_T_MAX;
     780      PyObject *subobj = NULL;
     781      int result;
     782  
     783      if (!stringlib_parse_args_finds(function_name, args, &subobj, &start, &end))
     784          return NULL;
     785      if (PyTuple_Check(subobj)) {
     786          Py_ssize_t i;
     787          for (i = 0; i < PyTuple_GET_SIZE(subobj); i++) {
     788              result = tailmatch(str, len, PyTuple_GET_ITEM(subobj, i),
     789                                 start, end, direction);
     790              if (result == -1)
     791                  return NULL;
     792              else if (result) {
     793                  Py_RETURN_TRUE;
     794              }
     795          }
     796          Py_RETURN_FALSE;
     797      }
     798      result = tailmatch(str, len, subobj, start, end, direction);
     799      if (result == -1) {
     800          if (PyErr_ExceptionMatches(PyExc_TypeError))
     801              PyErr_Format(PyExc_TypeError,
     802                           "%s first arg must be bytes or a tuple of bytes, "
     803                           "not %s",
     804                           function_name, Py_TYPE(subobj)->tp_name);
     805          return NULL;
     806      }
     807      else
     808          return PyBool_FromLong(result);
     809  }
     810  
     811  PyDoc_STRVAR_shared(_Py_startswith__doc__,
     812  "B.startswith(prefix[, start[, end]]) -> bool\n\
     813  \n\
     814  Return True if B starts with the specified prefix, False otherwise.\n\
     815  With optional start, test B beginning at that position.\n\
     816  With optional end, stop comparing B at that position.\n\
     817  prefix can also be a tuple of bytes to try.");
     818  
     819  PyObject *
     820  _Py_bytes_startswith(const char *str, Py_ssize_t len, PyObject *args)
     821  {
     822      return _Py_bytes_tailmatch(str, len, "startswith", args, -1);
     823  }
     824  
     825  PyDoc_STRVAR_shared(_Py_endswith__doc__,
     826  "B.endswith(suffix[, start[, end]]) -> bool\n\
     827  \n\
     828  Return True if B ends with the specified suffix, False otherwise.\n\
     829  With optional start, test B beginning at that position.\n\
     830  With optional end, stop comparing B at that position.\n\
     831  suffix can also be a tuple of bytes to try.");
     832  
     833  PyObject *
     834  _Py_bytes_endswith(const char *str, Py_ssize_t len, PyObject *args)
     835  {
     836      return _Py_bytes_tailmatch(str, len, "endswith", args, +1);
     837  }