mercurial/base85.c
changeset 32407 008d37c4d783
parent 32406 a9c71d578a1c
child 32408 3b88a7fa97d8
equal deleted inserted replaced
32406:a9c71d578a1c 32407:008d37c4d783
     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 #define PY_SSIZE_T_CLEAN
       
    13 #include <Python.h>
       
    14 
       
    15 #include "util.h"
       
    16 
       
    17 static const char b85chars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
       
    18 	"abcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~";
       
    19 static char b85dec[256];
       
    20 
       
    21 static void b85prep(void)
       
    22 {
       
    23 	unsigned i;
       
    24 
       
    25 	memset(b85dec, 0, sizeof(b85dec));
       
    26 	for (i = 0; i < sizeof(b85chars); i++)
       
    27 		b85dec[(int)(b85chars[i])] = i + 1;
       
    28 }
       
    29 
       
    30 static PyObject *b85encode(PyObject *self, PyObject *args)
       
    31 {
       
    32 	const unsigned char *text;
       
    33 	PyObject *out;
       
    34 	char *dst;
       
    35 	Py_ssize_t len, olen, i;
       
    36 	unsigned int acc, val, ch;
       
    37 	int pad = 0;
       
    38 
       
    39 	if (!PyArg_ParseTuple(args, "s#|i", &text, &len, &pad))
       
    40 		return NULL;
       
    41 
       
    42 	if (pad)
       
    43 		olen = ((len + 3) / 4 * 5) - 3;
       
    44 	else {
       
    45 		olen = len % 4;
       
    46 		if (olen)
       
    47 			olen++;
       
    48 		olen += len / 4 * 5;
       
    49 	}
       
    50 	if (!(out = PyBytes_FromStringAndSize(NULL, olen + 3)))
       
    51 		return NULL;
       
    52 
       
    53 	dst = PyBytes_AsString(out);
       
    54 
       
    55 	while (len) {
       
    56 		acc = 0;
       
    57 		for (i = 24; i >= 0; i -= 8) {
       
    58 			ch = *text++;
       
    59 			acc |= ch << i;
       
    60 			if (--len == 0)
       
    61 				break;
       
    62 		}
       
    63 		for (i = 4; i >= 0; i--) {
       
    64 			val = acc % 85;
       
    65 			acc /= 85;
       
    66 			dst[i] = b85chars[val];
       
    67 		}
       
    68 		dst += 5;
       
    69 	}
       
    70 
       
    71 	if (!pad)
       
    72 		_PyBytes_Resize(&out, olen);
       
    73 
       
    74 	return out;
       
    75 }
       
    76 
       
    77 static PyObject *b85decode(PyObject *self, PyObject *args)
       
    78 {
       
    79 	PyObject *out;
       
    80 	const char *text;
       
    81 	char *dst;
       
    82 	Py_ssize_t len, i, j, olen, cap;
       
    83 	int c;
       
    84 	unsigned int acc;
       
    85 
       
    86 	if (!PyArg_ParseTuple(args, "s#", &text, &len))
       
    87 		return NULL;
       
    88 
       
    89 	olen = len / 5 * 4;
       
    90 	i = len % 5;
       
    91 	if (i)
       
    92 		olen += i - 1;
       
    93 	if (!(out = PyBytes_FromStringAndSize(NULL, olen)))
       
    94 		return NULL;
       
    95 
       
    96 	dst = PyBytes_AsString(out);
       
    97 
       
    98 	i = 0;
       
    99 	while (i < len)
       
   100 	{
       
   101 		acc = 0;
       
   102 		cap = len - i - 1;
       
   103 		if (cap > 4)
       
   104 			cap = 4;
       
   105 		for (j = 0; j < cap; i++, j++)
       
   106 		{
       
   107 			c = b85dec[(int)*text++] - 1;
       
   108 			if (c < 0)
       
   109 				return PyErr_Format(
       
   110 					PyExc_ValueError,
       
   111 					"bad base85 character at position %d",
       
   112 					(int)i);
       
   113 			acc = acc * 85 + c;
       
   114 		}
       
   115 		if (i++ < len)
       
   116 		{
       
   117 			c = b85dec[(int)*text++] - 1;
       
   118 			if (c < 0)
       
   119 				return PyErr_Format(
       
   120 					PyExc_ValueError,
       
   121 					"bad base85 character at position %d",
       
   122 					(int)i);
       
   123 			/* overflow detection: 0xffffffff == "|NsC0",
       
   124 			 * "|NsC" == 0x03030303 */
       
   125 			if (acc > 0x03030303 || (acc *= 85) > 0xffffffff - c)
       
   126 				return PyErr_Format(
       
   127 					PyExc_ValueError,
       
   128 					"bad base85 sequence at position %d",
       
   129 					(int)i);
       
   130 			acc += c;
       
   131 		}
       
   132 
       
   133 		cap = olen < 4 ? olen : 4;
       
   134 		olen -= cap;
       
   135 		for (j = 0; j < 4 - cap; j++)
       
   136 			acc *= 85;
       
   137 		if (cap && cap < 4)
       
   138 			acc += 0xffffff >> (cap - 1) * 8;
       
   139 		for (j = 0; j < cap; j++)
       
   140 		{
       
   141 			acc = (acc << 8) | (acc >> 24);
       
   142 			*dst++ = acc;
       
   143 		}
       
   144 	}
       
   145 
       
   146 	return out;
       
   147 }
       
   148 
       
   149 static char base85_doc[] = "Base85 Data Encoding";
       
   150 
       
   151 static PyMethodDef methods[] = {
       
   152 	{"b85encode", b85encode, METH_VARARGS,
       
   153 	 "Encode text in base85.\n\n"
       
   154 	 "If the second parameter is true, pad the result to a multiple of "
       
   155 	 "five characters.\n"},
       
   156 	{"b85decode", b85decode, METH_VARARGS, "Decode base85 text.\n"},
       
   157 	{NULL, NULL}
       
   158 };
       
   159 
       
   160 static const int version = 1;
       
   161 
       
   162 #ifdef IS_PY3K
       
   163 static struct PyModuleDef base85_module = {
       
   164 	PyModuleDef_HEAD_INIT,
       
   165 	"base85",
       
   166 	base85_doc,
       
   167 	-1,
       
   168 	methods
       
   169 };
       
   170 
       
   171 PyMODINIT_FUNC PyInit_base85(void)
       
   172 {
       
   173 	PyObject *m;
       
   174 	b85prep();
       
   175 
       
   176 	m = PyModule_Create(&base85_module);
       
   177 	PyModule_AddIntConstant(m, "version", version);
       
   178 	return m;
       
   179 }
       
   180 #else
       
   181 PyMODINIT_FUNC initbase85(void)
       
   182 {
       
   183 	PyObject *m;
       
   184 	m = Py_InitModule3("base85", methods, base85_doc);
       
   185 
       
   186 	b85prep();
       
   187 	PyModule_AddIntConstant(m, "version", version);
       
   188 }
       
   189 #endif