Coverage Report

Created: 2025-07-04 06:49

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