forked from dhondta/python-codext
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbase91.py
More file actions
113 lines (100 loc) · 4.25 KB
/
Copy pathbase91.py
File metadata and controls
113 lines (100 loc) · 4.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# -*- coding: UTF-8 -*-
"""Base91 Codec - base91 content encoding.
This codec:
- en/decodes strings from str to str
- en/decodes strings from bytes to bytes
- decodes file content to str (read)
- encodes file content from str to bytes (write)
"""
from ._base import _get_charset, digits, lower, main, upper
from ..__common__ import *
# no __examples__ ; handled manually in tests/test_base.py
__guess__ = ["base91", "base91-inv", "base91-alt", "base91-alt-inv"]
B91 = {
r'': upper + lower + digits + "!#$%&()*+,./:;<=>?@[]^_`{|}~\"",
r'[-_]inv(erted)?$': digits + upper + lower + "!#$%&()*+,./:;<=>?@[]^_`{|}~\"",
r'[-_]alt(ernate)?$': "!#$%&'()*+,-./" + digits + ":;<=>?@" + upper + "[\\]^_" + lower + "{|}",
r'[-_]alt(ernate)?[-_]inv(erted)?$': "!#$%&'()*+,-./" + upper + ":;<=>?@" + lower + "[\\]^_" + digits + "{|}",
}
__chr = lambda c: chr(c) if isinstance(c, int) else c
__ord = lambda c: ord(c) if not isinstance(c, int) else c
def base91_encode(mode):
b91 = _get_charset(B91, mode)
def encode(text, errors="strict"):
t, s, bits = b(text), "", ""
if re.search(r'[-_]alt(ernate)?$', mode):
while len(bits) < 13 and t:
bits += "{:08b}".format(__ord(t[0]))
t = t[1:]
while len(bits) > 13 or t:
n = int(bits[:13], 2)
s += b91[n // 91] + b91[n % 91]
bits = bits[13:]
while len(bits) < 13 and t:
bits += "{:08b}".format(__ord(t[0]))
t = t[1:]
if len(bits) > 0:
if len(bits) < 7:
bits += "0" * (6 - len(bits))
s += b91[int(bits, 2)]
else:
bits += "0" * (13 - len(bits))
n = int(bits, 2)
s += b91[n // 91] + b91[n % 91]
else:
for c in t:
bits = bin(__ord(c))[2:].zfill(8) + bits
if len(bits) > 13:
n = int(bits[-13:], 2)
if n > 88:
bits = bits[:-13]
else:
n = int(bits[-14:], 2)
bits = bits[:-14]
s += b91[n % 91] + b91[n // 91]
if len(bits) > 0:
n = int(bits, 2)
s += b91[n % 91]
if len(bits) > 7 or n > 90:
s += b91[n // 91]
return s, len(t)
return encode
def base91_decode(mode):
b91 = {c: i for i, c in enumerate(_get_charset(B91, mode))}
def decode(text, errors="strict"):
t, s, bits, alt = b(_stripl(text, True, True)), "", "", re.search(r'[-_]alt(ernate)?$', mode) is not None
ehandler = handle_error("base91", errors, decode=True)
for i in range(0, len(t), 2):
try:
n = b91[__chr(t[i])] * [1, 91][alt]
except KeyError:
ehandler(__chr(t[i]), i, s)
try:
j = i + 1
n += b91[__chr(t[j])] * [91, 1][alt]
except IndexError:
pass
except KeyError:
ehandler(__chr(t[j]), j, s)
if alt:
bits += "{:013b}".format(n)
while 8 <= len(bits):
s += chr(int(bits[0:8], 2))
bits = bits[8:]
else:
bits = bin(n)[2:].zfill([14, 13][n & 8191 > 88]) + bits
while len(bits) > 8:
s += chr(int(bits[-8:], 2))
bits = bits[:-8]
if alt and len(t) % 2 == 1:
bits += "{:06b}".format(b91[__chr(t[-1])])
while 8 <= len(bits):
s += chr(int(bits[:8], 2))
bits = bits[8:]
elif not alt and len(bits) > 0 and not set(bits) == {"0"}:
s += chr(int(bits, 2))
return s.rstrip("\0"), len(t)
return decode
add("base91", base91_encode, base91_decode, r"^base[-_]?91((?:|[-_]alt(?:ernate)?)(?:|[-_]inv(?:erted)?)?)$",
entropy=6.5, expansion_factor=1.231)
main91 = main(91, "<http://base91.sourceforge.net/>")