merged now fully working base85 codec, though currently unused.
authorThomas Arendsen Hein <thomas@intevation.de>
Sun, 08 Oct 2006 10:56:21 +0200
changeset 3290 ae8583e746f1
parent 3289 48ae77d1e083 (current diff)
parent 3288 e93c926e069e (diff)
child 3291 0b5d626b354e
merged now fully working base85 codec, though currently unused.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/base85.c	Sun Oct 08 10:56:21 2006 +0200
@@ -0,0 +1,155 @@
+/*
+ base85 codec
+
+ Copyright 2006 Brendan Cully <brendan@kublai.com>
+
+ This software may be used and distributed according to the terms of
+ the GNU General Public License, incorporated herein by reference.
+
+ Largely based on git's implementation
+*/
+
+#include <Python.h>
+
+static const char b85chars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+	"abcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~";
+static char b85dec[256];
+
+static void
+b85prep(void)
+{
+	int i;
+
+	memset(b85dec, 0, sizeof(b85dec));
+	for (i = 0; i < sizeof(b85chars); i++)
+		b85dec[(int)(b85chars[i])] = i + 1;
+}
+
+static PyObject *
+b85encode(PyObject *self, PyObject *args)
+{
+	const unsigned char *text;
+	PyObject *out;
+	char *dst;
+	int len, olen, i;
+	unsigned int acc, val, ch;
+        int pad = 0;
+
+	if (!PyArg_ParseTuple(args, "s#|i", &text, &len, &pad))
+		return NULL;
+
+        if (pad)
+                olen = ((len + 3) / 4 * 5) - 3;
+        else {
+                olen = len % 4;
+                if (olen)
+                        olen++;
+                olen += len / 4 * 5;
+        }
+	if (!(out = PyString_FromStringAndSize(NULL, olen + 3)))
+		return NULL;
+
+	dst = PyString_AS_STRING(out);
+
+	while (len) {
+		acc = 0;
+		for (i = 24; i >= 0; i -= 8) {
+			ch = *text++;
+			acc |= ch << i;
+			if (--len == 0)
+				break;
+		}
+		for (i = 4; i >= 0; i--) {
+			val = acc % 85;
+			acc /= 85;
+			dst[i] = b85chars[val];
+		}
+		dst += 5;
+	}
+
+        if (!pad)
+                _PyString_Resize(&out, olen);
+
+	return out;
+}
+
+static PyObject *
+b85decode(PyObject *self, PyObject *args)
+{
+	PyObject *out;
+	const char *text;
+	char *dst;
+	int len, i, j, olen, c, cap;
+	unsigned int acc;
+
+	if (!PyArg_ParseTuple(args, "s#", &text, &len))
+		return NULL;
+
+	olen = len / 5 * 4;
+	i = len % 5;
+	if (i)
+		olen += i - 1;
+	if (!(out = PyString_FromStringAndSize(NULL, olen)))
+		return NULL;
+
+	dst = PyString_AS_STRING(out);
+
+	i = 0;
+	while (i < len)
+	{
+		acc = 0;
+		cap = len - i - 1;
+		if (cap > 4)
+			cap = 4;
+		for (j = 0; j < cap; i++, j++)
+		{
+			c = b85dec[(int)*text++] - 1;
+			if (c < 0)
+				return PyErr_Format(PyExc_ValueError, "Bad base85 character at position %d", i);
+			acc = acc * 85 + c;
+		}
+		if (i++ < len)
+		{
+			c = b85dec[(int)*text++] - 1;
+			if (c < 0)
+				return PyErr_Format(PyExc_ValueError, "Bad base85 character at position %d", i);
+			/* overflow detection: 0xffffffff == "|NsC0",
+			 * "|NsC" == 0x03030303 */
+			if (acc > 0x03030303 || (acc *= 85) > 0xffffffff - c)
+				return PyErr_Format(PyExc_ValueError, "Bad base85 sequence at position %d", i);
+			acc += c;
+		}
+
+		cap = olen < 4 ? olen : 4;
+		olen -= cap;
+		for (j = 0; j < 4 - cap; j++)
+			acc *= 85;
+		if (cap && cap < 4)
+			acc += 0xffffff >> (cap - 1) * 8;
+		for (j = 0; j < cap; j++)
+		{
+			acc = (acc << 8) | (acc >> 24);
+			*dst++ = acc;
+		}
+	}
+
+	return out;
+}
+
+static char base85_doc[] = "Base85 Data Encoding";
+
+static PyMethodDef methods[] = {
+	{"b85encode", b85encode, METH_VARARGS,
+         "Encode text in base85.\n\n"
+         "If the second parameter is true, pad the result to a multiple of "
+         "five characters.\n"},
+	{"b85decode", b85decode, METH_VARARGS, "Decode base85 text.\n"},
+	{NULL, NULL}
+};
+
+PyMODINIT_FUNC initbase85(void)
+{
+	Py_InitModule3("base85", methods, base85_doc);
+
+	b85prep();
+}
--- a/setup.py	Sun Oct 08 10:55:11 2006 +0200
+++ b/setup.py	Sun Oct 08 10:56:21 2006 +0200
@@ -89,7 +89,8 @@
       license='GNU GPL',
       packages=['mercurial', 'mercurial.hgweb', 'hgext'],
       ext_modules=[Extension('mercurial.mpatch', ['mercurial/mpatch.c']),
-                   Extension('mercurial.bdiff', ['mercurial/bdiff.c'])],
+                   Extension('mercurial.bdiff', ['mercurial/bdiff.c']),
+                   Extension('mercurial.base85', ['mercurial/base85.c'])],
       data_files=[(os.path.join('mercurial', root),
                    [os.path.join(root, file_) for file_ in files])
                   for root, dirs, files in os.walk('templates')],