From e977c210cbadf5d4a91eefa94b9b10da484f6ef1 Mon Sep 17 00:00:00 2001
From: Barry Warsaw <barry@python.org>
Date: Tue, 15 Aug 2000 06:07:13 +0000
Subject: After a brief conversation and code review with TP, adding two very
 commonly used functions to convert an arbitrary binary string into a
 hexadecimal digit representation and back again.  These are often (and often
 differently) implemented in Python.  Best to have one common fast
 implementation.  Specifically,

binascii_hexlify(): a.k.a. b2a_hex() to return the hex representation
of binary data.

binascii_unhexlify(): a.k.a. a2b_hex() to do the inverse conversion
(hex digits to binary data).  The argument must have an even length,
and must contain only hex digits, otherwise a TypeError is raised.
---
 Modules/binascii.c | 148 +++++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 126 insertions(+), 22 deletions(-)

diff --git a/Modules/binascii.c b/Modules/binascii.c
index 50f8b76..89ccc79 100644
--- a/Modules/binascii.c
+++ b/Modules/binascii.c
@@ -874,31 +874,135 @@ binascii_crc32(PyObject *self, PyObject *args)
 	return Py_BuildValue("l", crc ^ 0xFFFFFFFFUL);
 }
 
+
+static PyObject *
+binascii_hexlify(PyObject *self, PyObject *args)
+{
+	char* argbuf;
+	int arglen;
+	PyObject *retval;
+	char* retbuf;
+	int i, j;
+
+	if (!PyArg_ParseTuple(args, "t#:b2a_hex", &argbuf, &arglen))
+		return NULL;
+
+	retval = PyString_FromStringAndSize(NULL, arglen*2);
+	if (!retval)
+		return NULL;
+	retbuf = PyString_AsString(retval);
+	if (!retbuf)
+		goto finally;
+
+	/* make hex version of string, taken from shamodule.c */
+	for (i=j=0; i < arglen; i++) {
+		char c;
+		c = (argbuf[i] >> 4) & 0xf;
+		c = (c>9) ? c+'a'-10 : c + '0';
+		retbuf[j++] = c;
+		c = argbuf[i] & 0xf;
+		c = (c>9) ? c+'a'-10 : c + '0';
+		retbuf[j++] = c;
+	}
+	return retval;
+
+  finally:
+	Py_DECREF(retval);
+	return NULL;
+}
+
+static char doc_hexlify[] =
+"b2a_hex(data) -> s; Hexadecimal representation of binary data.\n\
+\n\
+This function is also available as \"hexlify()\".";
+
+
+static int
+to_int(char 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 *
+binascii_unhexlify(PyObject *self, PyObject *args)
+{
+	char* argbuf;
+	int arglen;
+	PyObject *retval;
+	char* retbuf;
+	int i, j;
+
+	if (!PyArg_ParseTuple(args, "s#:a2b_hex", &argbuf, &arglen))
+		return NULL;
+
+	/* XXX What should we do about odd-lengthed strings?  Should we add
+	 * an implicit leading zero, or a trailing zero?  For now, raise an
+	 * exception.
+	 */
+	if (arglen % 2) {
+		PyErr_SetString(PyExc_TypeError, "odd lengthed string");
+		return NULL;
+	}
+
+	retval = PyString_FromStringAndSize(NULL, (arglen/2));
+	if (!retval)
+		return NULL;
+	retbuf = PyString_AsString(retval);
+	if (!retbuf)
+		goto finally;
+
+	for (i=j=0; i < arglen; i += 2) {
+		int top = to_int(Py_CHARMASK(argbuf[i]));
+		int bot = to_int(Py_CHARMASK(argbuf[i+1]));
+		if (top == -1 || bot == -1) {
+			PyErr_SetString(PyExc_TypeError,
+					"non-hexadecimal digit found");
+			goto finally;
+		}
+		retbuf[j++] = (top << 4) + bot;
+	}
+	return retval;
+
+  finally:
+	Py_DECREF(retval);
+	return NULL;
+}
+
+static char doc_unhexlify[] =
+"a2b_hex(hexstr) -> s; Binary data of hexadecimal representation.\n\
+\n\
+hexstr must contain an even number of hex digits (upper or lower case).\n\
+This function is also available as \"unhexlify()\"";
+
+
 /* List of functions defined in the module */
 
 static struct PyMethodDef binascii_module_methods[] = {
-	{"a2b_uu",		binascii_a2b_uu,	
-	 METH_VARARGS,	doc_a2b_uu},
-	{"b2a_uu",		binascii_b2a_uu,	
-	 METH_VARARGS,	doc_b2a_uu},
-	{"a2b_base64",		binascii_a2b_base64,	
-	 METH_VARARGS,
-	 doc_a2b_base64},
-	{"b2a_base64",		binascii_b2a_base64,	
-	 METH_VARARGS, doc_b2a_base64},
-	{"a2b_hqx",		binascii_a2b_hqx,	
-	 METH_VARARGS, doc_a2b_hqx},
-	{"b2a_hqx",		binascii_b2a_hqx,	
-	 METH_VARARGS, doc_b2a_hqx},
-	{"rlecode_hqx",		binascii_rlecode_hqx,	
-	 METH_VARARGS, doc_rlecode_hqx},
-	{"rledecode_hqx",	binascii_rledecode_hqx,	
-	 METH_VARARGS, doc_rledecode_hqx},
-	{"crc_hqx",		binascii_crc_hqx,	
-	 METH_VARARGS,	doc_crc_hqx},
-	{"crc32",		binascii_crc32,		
-	 METH_VARARGS, doc_crc32},
-	{NULL,			NULL}		/* sentinel */
+	{"a2b_uu",     binascii_a2b_uu,     METH_VARARGS, doc_a2b_uu},
+	{"b2a_uu",     binascii_b2a_uu,     METH_VARARGS, doc_b2a_uu},
+	{"a2b_base64", binascii_a2b_base64, METH_VARARGS, doc_a2b_base64},
+	{"b2a_base64", binascii_b2a_base64, METH_VARARGS, doc_b2a_base64},
+	{"a2b_hqx",    binascii_a2b_hqx,    METH_VARARGS, doc_a2b_hqx},
+	{"b2a_hqx",    binascii_b2a_hqx,    METH_VARARGS, doc_b2a_hqx},
+	{"b2a_hex",    binascii_hexlify,    METH_VARARGS, doc_hexlify},
+	{"a2b_hex",    binascii_unhexlify,  METH_VARARGS, doc_unhexlify},
+	{"hexlify",    binascii_hexlify,    METH_VARARGS, doc_hexlify},
+	{"unhexlify",  binascii_unhexlify,  METH_VARARGS, doc_unhexlify},
+	{"rlecode_hqx",   binascii_rlecode_hqx, METH_VARARGS, doc_rlecode_hqx},
+	{"rledecode_hqx", binascii_rledecode_hqx, METH_VARARGS,
+	 doc_rledecode_hqx},
+	{"crc_hqx",    binascii_crc_hqx,    METH_VARARGS, doc_crc_hqx},
+	{"crc32",      binascii_crc32,      METH_VARARGS, doc_crc32},
+	{NULL, NULL}			     /* sentinel */
 };
 
 
-- 
cgit v0.12

