mercurial/base85.c
changeset 3279 1f2c3983a6c5
child 3287 e93c926e069e
equal deleted inserted replaced
3278:4b2d3c8a6195 3279:1f2c3983a6c5
       
     1 /*
       
     2  base85 codec
       
     3 
       
     4  Copyright 2006 Brendan Cully <brendan@kublai.com>
       
     5 
       
     6  This software may be used and distributed according to the terms of
       
     7  the GNU General Public License, incorporated herein by reference.
       
     8 
       
     9  Largely based on git's implementation
       
    10 */
       
    11 
       
    12 #include <Python.h>
       
    13 
       
    14 static const char b85chars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
       
    15 	"abcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~";
       
    16 static char b85dec[256];
       
    17 
       
    18 static void
       
    19 b85prep(void)
       
    20 {
       
    21 	int i;
       
    22 
       
    23 	memset(b85dec, 0, sizeof(b85dec));
       
    24 	for (i = 0; i < sizeof(b85chars); i++)
       
    25 		b85dec[(int)(b85chars[i])] = i + 1;
       
    26 }
       
    27 
       
    28 static PyObject *
       
    29 b85encode(PyObject *self, PyObject *args)
       
    30 {
       
    31 	const unsigned char *text;
       
    32 	PyObject *out;
       
    33 	char *dst;
       
    34 	int len, olen, i;
       
    35 	unsigned int acc, val, ch;
       
    36 
       
    37 	if (!PyArg_ParseTuple(args, "s#", &text, &len))
       
    38 		return NULL;
       
    39 
       
    40 	olen = (len + 3) / 4 * 5;
       
    41 	if (!(out = PyString_FromStringAndSize(NULL, olen)))
       
    42 		return NULL;
       
    43 
       
    44 	dst = PyString_AS_STRING(out);
       
    45 
       
    46 	while (len)
       
    47 	{
       
    48 		acc = 0;
       
    49 		for (i = 24; i >= 0; i -= 8) {
       
    50 			ch = *text++;
       
    51 			acc |= ch << i;
       
    52 			if (--len == 0)
       
    53 				break;
       
    54 		}
       
    55 		for (i = 4; i >= 0; i--) {
       
    56 			val = acc % 85;
       
    57 			acc /= 85;
       
    58 			dst[i] = b85chars[val];
       
    59 		}
       
    60 		dst += 5;
       
    61 	}
       
    62 
       
    63 	return out;
       
    64 }
       
    65 
       
    66 static PyObject *
       
    67 b85decode(PyObject *self, PyObject *args)
       
    68 {
       
    69 	PyObject *out;
       
    70 	const char *text;
       
    71 	char *dst;
       
    72 	int len, i, j, olen, c;
       
    73 	unsigned int acc;
       
    74 
       
    75 	if (!PyArg_ParseTuple(args, "s#", &text, &len))
       
    76 		return NULL;
       
    77 
       
    78 	olen = (len + 4) / 5 * 4;
       
    79 	if (!(out = PyString_FromStringAndSize(NULL, olen)))
       
    80 		return NULL;
       
    81 
       
    82 	dst = PyString_AS_STRING(out);
       
    83 
       
    84 	for (i = 1; len; i++)
       
    85 	{
       
    86 		acc = 0;
       
    87 		for (j = 0; j < 4 && --len; j++)
       
    88 		{
       
    89 			c = b85dec[(int)*text++] - 1;
       
    90 			if (c < 0)
       
    91 				return PyErr_Format(PyExc_ValueError, "Bad base85 character at position %d", i);
       
    92 			acc = acc * 85 + c;
       
    93 		}
       
    94 		if (len--)
       
    95 		{
       
    96 			c = b85dec[(int)*text++] - 1;
       
    97 			if (c < 0)
       
    98 				return PyErr_Format(PyExc_ValueError, "Bad base85 character at position %d", i);
       
    99 		}
       
   100 		else
       
   101 			c = 0;
       
   102 		/* overflow detection: 0xffffffff == "|NsC0",
       
   103 		 * "|NsC" == 0x03030303 */
       
   104 		if (acc > 0x03030303 || (acc *= 85) > 0xffffffff - c)
       
   105 			return PyErr_Format(PyExc_ValueError, "Bad base85 sequence at position %d", i);
       
   106 		
       
   107 		acc += c;
       
   108 
       
   109 		for (j = 0; j < 4; j++)
       
   110 		{
       
   111 			acc = (acc << 8) | (acc >> 24);
       
   112 			*dst++ = (char)acc;
       
   113 		}
       
   114 	}
       
   115 
       
   116 	return out;
       
   117 }
       
   118 
       
   119 static char base85_doc[] = "Base85 Data Encoding";
       
   120 
       
   121 static PyMethodDef methods[] = {
       
   122 	{"b85encode", b85encode, METH_VARARGS, "encode text in base85\n"},
       
   123 	{"b85decode", b85decode, METH_VARARGS, "decode base85 text\n"},
       
   124 	{NULL, NULL}
       
   125 };
       
   126 
       
   127 PyMODINIT_FUNC initbase85(void)
       
   128 {
       
   129 	Py_InitModule3("base85", methods, base85_doc);
       
   130 
       
   131 	b85prep();
       
   132 }