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)
|
||||
#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):
|
||||
self.assertEqual(bytes.join([]), bytes())
|
||||
self.assertEqual(bytes.join([bytes()]), bytes())
|
||||
|
|
|
@ -970,6 +970,71 @@ bytes_join(PyObject *cls, PyObject *it)
|
|||
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 = {
|
||||
(lenfunc)bytes_length, /* sq_length */
|
||||
(binaryfunc)bytes_concat, /* sq_concat */
|
||||
|
@ -1002,6 +1067,7 @@ static PyMethodDef
|
|||
bytes_methods[] = {
|
||||
{"decode", (PyCFunction)bytes_decode, METH_VARARGS, decode_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},
|
||||
{NULL}
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue