Skip to content

Commit b5ffb40

Browse files
committed
Fixed base91
1 parent 2fcdae8 commit b5ffb40

6 files changed

Lines changed: 94 additions & 24 deletions

File tree

codext/VERSION.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.5.3
1+
1.5.4

codext/base/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# -*- coding: UTF-8 -*-
22
from .ascii85 import *
33
from .base85 import *
4+
from .base91 import *
45
from .base100 import *
56
from .base122 import *
67
from .baseN import *

codext/base/base100.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,7 @@
1313

1414

1515
if PY3:
16-
class Base100Error(ValueError):
17-
pass
18-
19-
20-
class Base100DecodeError(Base100Error):
16+
class Base100DecodeError(ValueError):
2117
pass
2218

2319

codext/base/base91.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# -*- coding: UTF-8 -*-
2+
"""Base91 Codec - base91 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 string import ascii_lowercase as lower, ascii_uppercase as upper, digits
11+
12+
from ..__common__ import *
13+
14+
15+
B91 = {
16+
'': upper + lower + digits + "!#$%&()*+,./:;<=>?@[]^_`{|}~\"",
17+
'inv': lower + upper + digits + "!#$%&()*+,./:;<=>?@[]^_`{|}~\"",
18+
}
19+
20+
21+
__chr = lambda c: chr(c) if isinstance(c, int) else c
22+
__ord = lambda c: ord(c) if not isinstance(c, int) else c
23+
24+
25+
class Base91DecodeError(ValueError):
26+
pass
27+
28+
29+
def base91_encode(mode):
30+
b91 = B91[["inv", ""][mode == ""]]
31+
def encode(text, errors="strict"):
32+
t = b(text)
33+
s = ""
34+
bits = ""
35+
for c in t:
36+
bits = bin(__ord(c))[2:].zfill(8) + bits
37+
if len(bits) > 13:
38+
n = int(bits[-13:], 2)
39+
if n > 88:
40+
bits = bits[:-13]
41+
else:
42+
n = int(bits[-14:], 2)
43+
bits = bits[:-14]
44+
s += b91[n % 91] + b91[n // 91]
45+
if len(bits) > 0:
46+
n = int(bits, 2)
47+
s += b91[n % 91]
48+
if len(bits) > 7 or n > 90:
49+
s += b91[n // 91]
50+
return s, len(t)
51+
return encode
52+
53+
54+
def base91_decode(mode):
55+
b91 = {c: i for i, c in enumerate(B91[["inv", ""][mode == ""]])}
56+
def decode(text, errors="strict"):
57+
t = b(text)
58+
s = ""
59+
bits = ""
60+
for i in range(0, len(t), 2):
61+
try:
62+
n = b91[__chr(t[i])]
63+
except KeyError:
64+
raise Base91DecodeError("'base91' codec can't decode character '%s' in position %d" % (__chr(t[i]), i))
65+
try:
66+
j = i + 1
67+
n += b91[__chr(t[j])] * 91
68+
except IndexError:
69+
pass
70+
except KeyError:
71+
raise Base91DecodeError("'base91' codec can't decode character '%s' in position %d" % (__chr(t[j]), j))
72+
bits = bin(n)[2:].zfill([14, 13][n & 8191 > 88]) + bits
73+
while len(bits) > 8:
74+
s += chr(int(bits[-8:], 2))
75+
bits = bits[:-8]
76+
if len(bits) > 0 and not set(bits) == {"0"}:
77+
s += chr(int(bits, 2))
78+
return s, len(t)
79+
return decode
80+
81+
82+
add("base91", base91_encode, base91_decode, r"^base[-_]?91(|[-_]inv(?:erted)?)$")
83+

codext/base/baseN.py

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -69,21 +69,6 @@
6969
base2n(B64, r"^base[-_]?64(|[-_]inv(?:erted)?|[-_](?:file|url)(?:safe)?)$")
7070

7171

72-
#FIXME
73-
#B85 = {
74-
# r'': "!\"#$%&'()*+,-./" + digits + ":;<=>?@" + upper + "[\\]^_`" + lower[:21],
75-
# r'[-_]z(eromq)?$': digits + upper + lower + ".-:+=^!/*?&<>()[]{}@%$#",
76-
# r'[-_]rfc1924$': digits + upper + lower + "!#$%&()*+-;<=>?@^_`{|}~",
77-
#}
78-
#base(B85, r"^(?:ascii|base)[-_]?85(|[-_](?:z(?:eromq)?|rfc1924))$")
79-
80-
81-
B91 = {
82-
'': upper + lower + digits + "!#$%&()*+,./:;<=>?@[]^_`{|}~\"",
83-
'inv': lower + upper + digits + "!#$%&()*+,./:;<=>?@[]^_`{|}~\"",
84-
}
85-
base(B91, r"^base[-_]?91(|[-_]inv(?:erted)?)$")
86-
87-
8872
# generic base encodings, to be added after all others as they have the precedence
8973
base_generic()
74+

tests/test_base.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,14 +196,19 @@ def test_codec_base85(self):
196196
self.assertEqual(codecs.decode(b(B85), "base85"), b(STR))
197197

198198
def test_codec_base91(self):
199-
B91 = "BP*=!ijQolDVc>(pdD"
199+
B91 = ",X,<:WRT%yxth90oZB"
200200
self.assertEqual(codecs.encode(STR, "base91"), B91)
201201
self.assertEqual(codecs.encode(b(STR), "base91"), b(B91))
202202
self.assertEqual(codecs.decode(B91, "base91"), STR)
203203
self.assertEqual(codecs.decode(b(B91), "base91"), b(STR))
204-
B91 = "bp*=!IJqOLdvC>(PDd"
204+
B91 = ",x,<:wrt%YXTH90Ozb"
205205
self.assertEqual(codecs.encode(STR, "base91-inv"), B91)
206206
self.assertEqual(codecs.decode(B91, "base91-inv"), STR)
207+
self.assertIsNotNone(codecs.encode("\x00\x00", "base91"))
208+
self.assertIsNotNone(codecs.decode("abc", "base91"))
209+
self.assertIsNotNone(codecs.decode("AD", "base91"))
210+
self.assertRaises(ValueError, codecs.decode, "\xff", "base91")
211+
self.assertRaises(ValueError, codecs.decode, "a\xff", "base91")
207212

208213
def test_codec_base100(self):
209214
if PY3:

0 commit comments

Comments
 (0)