Skip to content

Commit 134a7c6

Browse files
committed
Added codec: base45
1 parent b9af1cb commit 134a7c6

3 files changed

Lines changed: 86 additions & 1 deletion

File tree

codext/base/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# -*- coding: UTF-8 -*-
22
from .ascii85 import *
3+
from .base45 import *
34
from .base85 import *
45
from .base91 import *
56
from .base100 import *

codext/base/base45.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# -*- coding: UTF-8 -*-
2+
"""Base45 Codec - base45 content encoding.
3+
4+
This codec:
5+
- en/decodes strings from str to str
6+
- en/decodes strings from bytes to bytes
7+
- decodes file content to str (read)
8+
- encodes file content from str to bytes (write)
9+
"""
10+
from ._base import digits, lower, upper
11+
from ..__common__ import *
12+
13+
14+
__examples__ = {
15+
'enc(base45|base-45|base_45)': {'this is a test!': "AWE+EDH44.OEOCC7WE QEX0"},
16+
'dec(base45)': {'BAD STRING\00': None},
17+
}
18+
__guess__ = ["base45", "base45-inv"]
19+
20+
21+
B45 = {
22+
'': digits + upper + " $%*+-./:",
23+
'inv': upper + digits + " $%*+-./:",
24+
}
25+
26+
27+
__chr = lambda c: chr(c) if isinstance(c, int) else c
28+
__ord = lambda c: ord(c) if not isinstance(c, int) else c
29+
30+
31+
class Base45DecodeError(ValueError):
32+
pass
33+
34+
35+
def base45_encode(mode):
36+
mode = mode.replace("inverted", "inv").replace("_", "-").lstrip("-")
37+
b45 = B45[['inv', ''][mode == ""]]
38+
def encode(text, errors="strict"):
39+
t, s = b(text), ""
40+
for i in range(0, len(text), 2):
41+
n = 256 * __ord(t[i])
42+
try:
43+
n += __ord(t[i+1])
44+
except IndexError:
45+
n = __ord(t[i])
46+
s += b45[n % 45] + b45[n // 45]
47+
break
48+
m = n // 45**2
49+
n -= m * 45**2
50+
s += b45[n % 45] + b45[n // 45] + b45[m]
51+
return s, len(text)
52+
return encode
53+
54+
55+
def base45_decode(mode):
56+
mode = mode.replace("inverted", "inv").replace("_", "-").lstrip("-")
57+
b45 = {c: i for i, c in enumerate(B45[['inv', ''][mode == ""]])}
58+
def decode(text, errors="strict"):
59+
t, s, err = b(text), "", "'base45' codec can't decode character '%s' in position %d"
60+
for i in range(0, len(text), 3):
61+
try:
62+
n = b45[__chr(t[i])]
63+
except KeyError:
64+
raise Base45DecodeError(err % (__chr(t[i]), i))
65+
try:
66+
j = i + 1
67+
n += 45 * b45[__chr(t[j])]
68+
except KeyError:
69+
raise Base45DecodeError(err % (__chr(t[j]), j))
70+
try:
71+
k = i + 2
72+
n += 45 ** 2 * b45[__chr(t[k])]
73+
except KeyError:
74+
raise Base45DecodeError(err % (__chr(t[k]), k))
75+
except IndexError:
76+
s += __chr(n)
77+
continue
78+
s += __chr(n // 256) + __chr(n % 256)
79+
return s, len(text)
80+
return decode
81+
82+
83+
add("base45", base45_encode, base45_decode, r"^base[-_]?45(|[-_]inv(?:erted)?)$")
84+

tests/test_generated.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def _template(self):
8484
with codecs.open(tfile, 'wb', encoding=ename) as f:
8585
f.write(b(s))
8686
with codecs.open(tfile, 'rb', encoding=ename) as f:
87-
s2 = f.read().strip(b("\x00"))
87+
s2 = f.read() if PY3 else f.read().rstrip("\x00")
8888
self.assertEqual(b(icdec(s2)), b(icdec(s)))
8989
os.remove(tfile)
9090
else:

0 commit comments

Comments
 (0)