mirror of https://github.com/python/cpython
Implement bytes.fromhex(), with tests.
This commit is contained in:
parent
88d65bd3e2
commit
0b9b9e0483
|
@ -396,6 +396,21 @@ class BytesTest(unittest.TestCase):
|
||||||
seq.append(alloc)
|
seq.append(alloc)
|
||||||
#print seq
|
#print seq
|
||||||
|
|
||||||
|
def test_fromhex(self):
|
||||||
|
self.assertRaises(TypeError, bytes.fromhex)
|
||||||
|
self.assertRaises(TypeError, bytes.fromhex, 1)
|
||||||
|
self.assertEquals(bytes.fromhex(''), bytes())
|
||||||
|
b = bytes([0x1a, 0x2b, 0x30])
|
||||||
|
self.assertEquals(bytes.fromhex('1a2B30'), b)
|
||||||
|
self.assertEquals(bytes.fromhex(' 1A 2B 30 '), b)
|
||||||
|
self.assertEquals(bytes.fromhex(buffer('')), bytes())
|
||||||
|
self.assertEquals(bytes.fromhex(buffer('0000')), bytes([0, 0]))
|
||||||
|
self.assertRaises(ValueError, bytes.fromhex, 'a')
|
||||||
|
self.assertRaises(ValueError, bytes.fromhex, 'rt')
|
||||||
|
self.assertRaises(ValueError, bytes.fromhex, '1a b cd')
|
||||||
|
self.assertRaises(ValueError, bytes.fromhex, '\x00')
|
||||||
|
self.assertRaises(ValueError, bytes.fromhex, '12 \x00 34')
|
||||||
|
|
||||||
def test_join(self):
|
def test_join(self):
|
||||||
self.assertEqual(bytes.join([]), bytes())
|
self.assertEqual(bytes.join([]), bytes())
|
||||||
self.assertEqual(bytes.join([bytes()]), bytes())
|
self.assertEqual(bytes.join([bytes()]), bytes())
|
||||||
|
|
|
@ -970,17 +970,82 @@ bytes_join(PyObject *cls, PyObject *it)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PyDoc_STRVAR(fromhex_doc,
|
||||||
|
"bytes.fromhex(string) -> bytes\n\
|
||||||
|
\n\
|
||||||
|
Create a bytes object from a string of hexadecimal numbers.\n\
|
||||||
|
Spaces between two numbers are accepted. Example:\n\
|
||||||
|
bytes.fromhex('10 2030') -> bytes([0x10, 0x20, 0x30]).");
|
||||||
|
|
||||||
|
static int
|
||||||
|
hex_digit_to_int(int c)
|
||||||
|
{
|
||||||
|
if (isdigit(c))
|
||||||
|
return c - '0';
|
||||||
|
else {
|
||||||
|
if (isupper(c))
|
||||||
|
c = tolower(c);
|
||||||
|
if (c >= 'a' && c <= 'f')
|
||||||
|
return c - 'a' + 10;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
bytes_fromhex(PyObject *cls, PyObject *args)
|
||||||
|
{
|
||||||
|
PyObject *newbytes;
|
||||||
|
char *hex, *buf;
|
||||||
|
Py_ssize_t len, byteslen, i, j;
|
||||||
|
int top, bot;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTuple(args, "s#:fromhex", &hex, &len))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
byteslen = len / 2; /* max length if there are no spaces */
|
||||||
|
|
||||||
|
newbytes = PyBytes_FromStringAndSize(NULL, byteslen);
|
||||||
|
if (!newbytes)
|
||||||
|
return NULL;
|
||||||
|
buf = PyBytes_AS_STRING(newbytes);
|
||||||
|
|
||||||
|
for (i = j = 0; ; i += 2) {
|
||||||
|
/* skip over spaces in the input */
|
||||||
|
while (Py_CHARMASK(hex[i]) == ' ')
|
||||||
|
i++;
|
||||||
|
if (i >= len)
|
||||||
|
break;
|
||||||
|
top = hex_digit_to_int(Py_CHARMASK(hex[i]));
|
||||||
|
bot = hex_digit_to_int(Py_CHARMASK(hex[i+1]));
|
||||||
|
if (top == -1 || bot == -1) {
|
||||||
|
PyErr_Format(PyExc_ValueError,
|
||||||
|
"non-hexadecimal number string '%c%c' found in "
|
||||||
|
"fromhex() arg at position %zd",
|
||||||
|
hex[i], hex[i+1], i);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
buf[j++] = (top << 4) + bot;
|
||||||
|
}
|
||||||
|
if (PyBytes_Resize(newbytes, j) < 0)
|
||||||
|
goto error;
|
||||||
|
return newbytes;
|
||||||
|
|
||||||
|
error:
|
||||||
|
Py_DECREF(newbytes);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static PySequenceMethods bytes_as_sequence = {
|
static PySequenceMethods bytes_as_sequence = {
|
||||||
(lenfunc)bytes_length, /*sq_length*/
|
(lenfunc)bytes_length, /* sq_length */
|
||||||
(binaryfunc)bytes_concat, /*sq_concat*/
|
(binaryfunc)bytes_concat, /* sq_concat */
|
||||||
(ssizeargfunc)bytes_repeat, /*sq_repeat*/
|
(ssizeargfunc)bytes_repeat, /* sq_repeat */
|
||||||
(ssizeargfunc)bytes_getitem, /*sq_item*/
|
(ssizeargfunc)bytes_getitem, /* sq_item */
|
||||||
0, /*sq_slice*/
|
0, /* sq_slice */
|
||||||
(ssizeobjargproc)bytes_setitem, /*sq_ass_item*/
|
(ssizeobjargproc)bytes_setitem, /* sq_ass_item */
|
||||||
0, /* sq_ass_slice */
|
0, /* sq_ass_slice */
|
||||||
(objobjproc)bytes_contains, /* sq_contains */
|
(objobjproc)bytes_contains, /* sq_contains */
|
||||||
(binaryfunc)bytes_iconcat, /* sq_inplace_concat */
|
(binaryfunc)bytes_iconcat, /* sq_inplace_concat */
|
||||||
(ssizeargfunc)bytes_irepeat, /* sq_inplace_repeat */
|
(ssizeargfunc)bytes_irepeat, /* sq_inplace_repeat */
|
||||||
};
|
};
|
||||||
|
|
||||||
static PyMappingMethods bytes_as_mapping = {
|
static PyMappingMethods bytes_as_mapping = {
|
||||||
|
@ -1002,6 +1067,7 @@ static PyMethodDef
|
||||||
bytes_methods[] = {
|
bytes_methods[] = {
|
||||||
{"decode", (PyCFunction)bytes_decode, METH_VARARGS, decode_doc},
|
{"decode", (PyCFunction)bytes_decode, METH_VARARGS, decode_doc},
|
||||||
{"__alloc__", (PyCFunction)bytes_alloc, METH_NOARGS, alloc_doc},
|
{"__alloc__", (PyCFunction)bytes_alloc, METH_NOARGS, alloc_doc},
|
||||||
|
{"fromhex", (PyCFunction)bytes_fromhex, METH_VARARGS|METH_CLASS, fromhex_doc},
|
||||||
{"join", (PyCFunction)bytes_join, METH_O|METH_CLASS, join_doc},
|
{"join", (PyCFunction)bytes_join, METH_O|METH_CLASS, join_doc},
|
||||||
{NULL}
|
{NULL}
|
||||||
};
|
};
|
||||||
|
@ -1032,7 +1098,7 @@ PyTypeObject PyBytes_Type = {
|
||||||
PyObject_GenericGetAttr, /* tp_getattro */
|
PyObject_GenericGetAttr, /* tp_getattro */
|
||||||
0, /* tp_setattro */
|
0, /* tp_setattro */
|
||||||
&bytes_as_buffer, /* tp_as_buffer */
|
&bytes_as_buffer, /* tp_as_buffer */
|
||||||
Py_TPFLAGS_DEFAULT, /* tp_flags */
|
Py_TPFLAGS_DEFAULT, /* tp_flags */
|
||||||
/* bytes is 'final' or 'sealed' */
|
/* bytes is 'final' or 'sealed' */
|
||||||
bytes_doc, /* tp_doc */
|
bytes_doc, /* tp_doc */
|
||||||
0, /* tp_traverse */
|
0, /* tp_traverse */
|
||||||
|
|
Loading…
Reference in New Issue