Branch data 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 : 0 : 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 : : 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 : : }
19 [ # # ]: 0 : if (seplen != 1) {
20 : 0 : PyErr_SetString(PyExc_ValueError, "sep must be length 1.");
21 : 0 : return NULL;
22 : : }
23 [ # # ]: 0 : if (PyUnicode_Check(sep)) {
24 [ # # ]: 0 : if (PyUnicode_READY(sep))
25 : 0 : return NULL;
26 [ # # ]: 0 : if (PyUnicode_KIND(sep) != PyUnicode_1BYTE_KIND) {
27 : 0 : PyErr_SetString(PyExc_ValueError, "sep must be ASCII.");
28 : 0 : return NULL;
29 : : }
30 : 0 : sep_char = PyUnicode_READ_CHAR(sep, 0);
31 : : }
32 [ # # ]: 0 : else if (PyBytes_Check(sep)) {
33 : 0 : 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 [ # # # # ]: 0 : if (sep_char > 127 && !return_bytes) {
40 : 0 : PyErr_SetString(PyExc_ValueError, "sep must be ASCII.");
41 : 0 : return NULL;
42 : : }
43 : : }
44 : : else {
45 : 0 : bytes_per_sep_group = 0;
46 : : }
47 : :
48 : 0 : unsigned int abs_bytes_per_sep = abs(bytes_per_sep_group);
49 : 0 : Py_ssize_t resultlen = 0;
50 [ # # # # ]: 0 : if (bytes_per_sep_group && arglen > 0) {
51 : : /* How many sep characters we'll be inserting. */
52 : 0 : resultlen = (arglen - 1) / abs_bytes_per_sep;
53 : : }
54 : : /* Bounds checking for our Py_ssize_t indices. */
55 [ # # ]: 0 : if (arglen >= PY_SSIZE_T_MAX / 2 - resultlen) {
56 : 0 : return PyErr_NoMemory();
57 : : }
58 : 0 : resultlen += arglen * 2;
59 : :
60 [ # # ]: 0 : if ((size_t)abs_bytes_per_sep >= (size_t)arglen) {
61 : 0 : bytes_per_sep_group = 0;
62 : 0 : abs_bytes_per_sep = 0;
63 : : }
64 : :
65 : : PyObject *retval;
66 : : Py_UCS1 *retbuf;
67 [ # # ]: 0 : if (return_bytes) {
68 : : /* If _PyBytes_FromSize() were public we could avoid malloc+copy. */
69 : 0 : retval = PyBytes_FromStringAndSize(NULL, resultlen);
70 [ # # ]: 0 : if (!retval) {
71 : 0 : return NULL;
72 : : }
73 : 0 : retbuf = (Py_UCS1 *)PyBytes_AS_STRING(retval);
74 : : }
75 : : else {
76 : 0 : retval = PyUnicode_New(resultlen, 127);
77 [ # # ]: 0 : if (!retval) {
78 : 0 : return NULL;
79 : : }
80 : 0 : retbuf = PyUnicode_1BYTE_DATA(retval);
81 : : }
82 : :
83 : : /* Hexlify */
84 : : Py_ssize_t i, j;
85 : : unsigned char c;
86 : :
87 [ # # ]: 0 : if (bytes_per_sep_group == 0) {
88 [ # # ]: 0 : for (i = j = 0; i < arglen; ++i) {
89 : : assert((j + 1) < resultlen);
90 : 0 : c = argbuf[i];
91 : 0 : retbuf[j++] = Py_hexdigits[c >> 4];
92 : 0 : retbuf[j++] = Py_hexdigits[c & 0x0f];
93 : : }
94 : : assert(j == resultlen);
95 : : }
96 : : else {
97 : : /* The number of complete chunk+sep periods */
98 : 0 : Py_ssize_t chunks = (arglen - 1) / abs_bytes_per_sep;
99 : : Py_ssize_t chunk;
100 : : unsigned int k;
101 : :
102 [ # # ]: 0 : if (bytes_per_sep_group < 0) {
103 : 0 : i = j = 0;
104 [ # # ]: 0 : for (chunk = 0; chunk < chunks; chunk++) {
105 [ # # ]: 0 : for (k = 0; k < abs_bytes_per_sep; k++) {
106 : 0 : c = argbuf[i++];
107 : 0 : retbuf[j++] = Py_hexdigits[c >> 4];
108 : 0 : retbuf[j++] = Py_hexdigits[c & 0x0f];
109 : : }
110 : 0 : retbuf[j++] = sep_char;
111 : : }
112 [ # # ]: 0 : while (i < arglen) {
113 : 0 : c = argbuf[i++];
114 : 0 : retbuf[j++] = Py_hexdigits[c >> 4];
115 : 0 : retbuf[j++] = Py_hexdigits[c & 0x0f];
116 : : }
117 : : assert(j == resultlen);
118 : : }
119 : : else {
120 : 0 : i = arglen - 1;
121 : 0 : j = resultlen - 1;
122 [ # # ]: 0 : for (chunk = 0; chunk < chunks; chunk++) {
123 [ # # ]: 0 : for (k = 0; k < abs_bytes_per_sep; k++) {
124 : 0 : c = argbuf[i--];
125 : 0 : retbuf[j--] = Py_hexdigits[c & 0x0f];
126 : 0 : retbuf[j--] = Py_hexdigits[c >> 4];
127 : : }
128 : 0 : retbuf[j--] = sep_char;
129 : : }
130 [ # # ]: 0 : while (i >= 0) {
131 : 0 : c = argbuf[i--];
132 : 0 : retbuf[j--] = Py_hexdigits[c & 0x0f];
133 : 0 : retbuf[j--] = Py_hexdigits[c >> 4];
134 : : }
135 : : assert(j == -1);
136 : : }
137 : : }
138 : :
139 : : #ifdef Py_DEBUG
140 : : if (!return_bytes) {
141 : : assert(_PyUnicode_CheckConsistency(retval, 1));
142 : : }
143 : : #endif
144 : :
145 : 0 : return retval;
146 : : }
147 : :
148 : 0 : PyObject * _Py_strhex(const char* argbuf, const Py_ssize_t arglen)
149 : : {
150 : 0 : 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 : 0 : PyObject* _Py_strhex_with_sep(const char* argbuf, const Py_ssize_t arglen,
163 : : PyObject* sep, const int bytes_per_group)
164 : : {
165 : 0 : 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 : 0 : PyObject* _Py_strhex_bytes_with_sep(const char* argbuf, const Py_ssize_t arglen,
171 : : PyObject* sep, const int bytes_per_group)
172 : : {
173 : 0 : return _Py_strhex_impl(argbuf, arglen, sep, bytes_per_group, 1);
174 : : }
|