LCOV - code coverage report
Current view: top level - Python - pystrhex.c (source / functions) Hit Total Coverage
Test: CPython lcov report Lines: 81 89 91.0 %
Date: 2022-07-07 18:19:46 Functions: 4 5 80.0 %

          Line data    Source code
       1             : /* Format bytes as hexadecimal */
       2             : 
       3             : #include "Python.h"
       4             : #include "pycore_strhex.h"        // _Py_strhex_with_sep()
       5             : #include <stdlib.h>               // abs()
       6             : 
       7       26750 : static PyObject *_Py_strhex_impl(const char* argbuf, const Py_ssize_t arglen,
       8             :                                  PyObject* sep, int bytes_per_sep_group,
       9             :                                  const int return_bytes)
      10             : {
      11       26750 :     assert(arglen >= 0);
      12             : 
      13       26750 :     Py_UCS1 sep_char = 0;
      14       26750 :     if (sep) {
      15          97 :         Py_ssize_t seplen = PyObject_Length((PyObject*)sep);
      16          97 :         if (seplen < 0) {
      17           2 :             return NULL;
      18             :         }
      19          95 :         if (seplen != 1) {
      20           4 :             PyErr_SetString(PyExc_ValueError, "sep must be length 1.");
      21           4 :             return NULL;
      22             :         }
      23          91 :         if (PyUnicode_Check(sep)) {
      24          79 :             if (PyUnicode_READY(sep))
      25           0 :                 return NULL;
      26          79 :             if (PyUnicode_KIND(sep) != PyUnicode_1BYTE_KIND) {
      27           2 :                 PyErr_SetString(PyExc_ValueError, "sep must be ASCII.");
      28           2 :                 return NULL;
      29             :             }
      30          77 :             sep_char = PyUnicode_READ_CHAR(sep, 0);
      31             :         }
      32          12 :         else if (PyBytes_Check(sep)) {
      33          12 :             sep_char = PyBytes_AS_STRING(sep)[0];
      34             :         }
      35             :         else {
      36           0 :             PyErr_SetString(PyExc_TypeError, "sep must be str or bytes.");
      37           0 :             return NULL;
      38             :         }
      39          89 :         if (sep_char > 127 && !return_bytes) {
      40           6 :             PyErr_SetString(PyExc_ValueError, "sep must be ASCII.");
      41           6 :             return NULL;
      42             :         }
      43             :     }
      44             :     else {
      45       26653 :         bytes_per_sep_group = 0;
      46             :     }
      47             : 
      48       26736 :     unsigned int abs_bytes_per_sep = abs(bytes_per_sep_group);
      49       26736 :     Py_ssize_t resultlen = 0;
      50       26736 :     if (bytes_per_sep_group && arglen > 0) {
      51             :         /* How many sep characters we'll be inserting. */
      52          79 :         resultlen = (arglen - 1) / abs_bytes_per_sep;
      53             :     }
      54             :     /* Bounds checking for our Py_ssize_t indices. */
      55       26736 :     if (arglen >= PY_SSIZE_T_MAX / 2 - resultlen) {
      56           0 :         return PyErr_NoMemory();
      57             :     }
      58       26736 :     resultlen += arglen * 2;
      59             : 
      60       26736 :     if ((size_t)abs_bytes_per_sep >= (size_t)arglen) {
      61          25 :         bytes_per_sep_group = 0;
      62          25 :         abs_bytes_per_sep = 0;
      63             :     }
      64             : 
      65             :     PyObject *retval;
      66             :     Py_UCS1 *retbuf;
      67       26736 :     if (return_bytes) {
      68             :         /* If _PyBytes_FromSize() were public we could avoid malloc+copy. */
      69         267 :         retval = PyBytes_FromStringAndSize(NULL, resultlen);
      70         267 :         if (!retval) {
      71           0 :             return NULL;
      72             :         }
      73         267 :         retbuf = (Py_UCS1 *)PyBytes_AS_STRING(retval);
      74             :     }
      75             :     else {
      76       26469 :         retval = PyUnicode_New(resultlen, 127);
      77       26469 :         if (!retval) {
      78           0 :             return NULL;
      79             :         }
      80       26469 :         retbuf = PyUnicode_1BYTE_DATA(retval);
      81             :     }
      82             : 
      83             :     /* Hexlify */
      84             :     Py_ssize_t i, j;
      85             :     unsigned char c;
      86             : 
      87       26736 :     if (bytes_per_sep_group == 0) {
      88     1781860 :         for (i = j = 0; i < arglen; ++i) {
      89     1755190 :             assert((j + 1) < resultlen);
      90     1755190 :             c = argbuf[i];
      91     1755190 :             retbuf[j++] = Py_hexdigits[c >> 4];
      92     1755190 :             retbuf[j++] = Py_hexdigits[c & 0x0f];
      93             :         }
      94       26671 :         assert(j == resultlen);
      95             :     }
      96             :     else {
      97             :         /* The number of complete chunk+sep periods */
      98          65 :         Py_ssize_t chunks = (arglen - 1) / abs_bytes_per_sep;
      99             :         Py_ssize_t chunk;
     100             :         unsigned int k;
     101             : 
     102          65 :         if (bytes_per_sep_group < 0) {
     103          13 :             i = j = 0;
     104          28 :             for (chunk = 0; chunk < chunks; chunk++) {
     105          53 :                 for (k = 0; k < abs_bytes_per_sep; k++) {
     106          38 :                     c = argbuf[i++];
     107          38 :                     retbuf[j++] = Py_hexdigits[c >> 4];
     108          38 :                     retbuf[j++] = Py_hexdigits[c & 0x0f];
     109             :                 }
     110          15 :                 retbuf[j++] = sep_char;
     111             :             }
     112          32 :             while (i < arglen) {
     113          19 :                 c = argbuf[i++];
     114          19 :                 retbuf[j++] = Py_hexdigits[c >> 4];
     115          19 :                 retbuf[j++] = Py_hexdigits[c & 0x0f];
     116             :             }
     117          13 :             assert(j == resultlen);
     118             :         }
     119             :         else {
     120          52 :             i = arglen - 1;
     121          52 :             j = resultlen - 1;
     122         393 :             for (chunk = 0; chunk < chunks; chunk++) {
     123         919 :                 for (k = 0; k < abs_bytes_per_sep; k++) {
     124         578 :                     c = argbuf[i--];
     125         578 :                     retbuf[j--] = Py_hexdigits[c & 0x0f];
     126         578 :                     retbuf[j--] = Py_hexdigits[c >> 4];
     127             :                 }
     128         341 :                 retbuf[j--] = sep_char;
     129             :             }
     130         182 :             while (i >= 0) {
     131         130 :                 c = argbuf[i--];
     132         130 :                 retbuf[j--] = Py_hexdigits[c & 0x0f];
     133         130 :                 retbuf[j--] = Py_hexdigits[c >> 4];
     134             :             }
     135          52 :             assert(j == -1);
     136             :         }
     137             :     }
     138             : 
     139             : #ifdef Py_DEBUG
     140       26736 :     if (!return_bytes) {
     141       26469 :         assert(_PyUnicode_CheckConsistency(retval, 1));
     142             :     }
     143             : #endif
     144             : 
     145       26736 :     return retval;
     146             : }
     147             : 
     148       15125 : PyObject * _Py_strhex(const char* argbuf, const Py_ssize_t arglen)
     149             : {
     150       15125 :     return _Py_strhex_impl(argbuf, arglen, NULL, 0, 0);
     151             : }
     152             : 
     153             : /* Same as above but returns a bytes() instead of str() to avoid the
     154             :  * need to decode the str() when bytes are needed. */
     155           0 : PyObject* _Py_strhex_bytes(const char* argbuf, const Py_ssize_t arglen)
     156             : {
     157           0 :     return _Py_strhex_impl(argbuf, arglen, NULL, 0, 1);
     158             : }
     159             : 
     160             : /* These variants include support for a separator between every N bytes: */
     161             : 
     162       11358 : PyObject* _Py_strhex_with_sep(const char* argbuf, const Py_ssize_t arglen,
     163             :                               PyObject* sep, const int bytes_per_group)
     164             : {
     165       11358 :     return _Py_strhex_impl(argbuf, arglen, sep, bytes_per_group, 0);
     166             : }
     167             : 
     168             : /* Same as above but returns a bytes() instead of str() to avoid the
     169             :  * need to decode the str() when bytes are needed. */
     170         267 : PyObject* _Py_strhex_bytes_with_sep(const char* argbuf, const Py_ssize_t arglen,
     171             :                                     PyObject* sep, const int bytes_per_group)
     172             : {
     173         267 :     return _Py_strhex_impl(argbuf, arglen, sep, bytes_per_group, 1);
     174             : }

Generated by: LCOV version 1.14