Coverage for src/lib.mys : 95%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1class Base64Error(Error):
2 message: string
4func _index_to_encoded(index: u8) -> u8:
5 if 0 <= index and index <= 25:
6 return u8(i32('A') + i32(index) - 0)
7 elif 26 <= index and index <= 51:
8 return u8(i32('a') + i32(index) - 26)
9 elif 52 <= index and index <= 61:
10 return u8(i32('0') + i32(index) - 52)
11 elif index == 62:
12 return u8(i32('+'))
13 elif index == 63:
14 return u8(i32('/'))
15 else:
16 return u8(i32('='))
18func _encoded_to_index(encoded: u8) -> u8:
19 if u8(i32('A')) <= encoded and encoded <= u8(i32('Z')):
20 return encoded - u8(i32('A'))
21 elif u8(i32('a')) <= encoded and encoded <= u8(i32('z')):
22 return 26 + encoded - u8(i32('a'))
23 elif u8(i32('0')) <= encoded and encoded <= u8(i32('9')):
24 return 52 + encoded - u8(i32('0'))
25 elif encoded == u8(i32('+')):
26 return 62
27 elif encoded == u8(i32('/')):
28 return 63
29 elif encoded == u8(i32('=')):
30 return 64
31 else:
32 raise Base64Error(f"Invalid value {encoded}.")
34func encode(data: bytes) -> string:
35 """Encode given data.
37 """
39 return string(encode_bytes(data))
41func decode(data: string) -> bytes:
42 """Decode given base64 encoded data.
44 """
46 return decode_bytes(data.to_utf8())
48func encode_bytes(data: bytes) -> bytes:
49 """Encode given data.
51 """
53 encoded = b""
54 length = data.length()
55 encoded.reserve(4 * ((length + 2) / 3))
57 for i in range(0, length, 3):
58 index = (data[i + 0] & 0xfc) >> 2
59 encoded += _index_to_encoded(index)
60 index = ((data[i + 0] & 0x03) << 4)
62 if i + 1 < length:
63 index |= (data[i + 1] & 0xf0) >> 4
64 encoded += _index_to_encoded(index)
65 index = (data[i + 1] & 0x0f) << 2
67 if i + 2 < length:
68 index |= (data[i + 2] & 0xc0) >> 6
69 encoded += _index_to_encoded(index)
70 index = data[i + 2] & 0x3f
71 encoded += _index_to_encoded(index)
72 else:
73 encoded += _index_to_encoded(index)
74 encoded += _index_to_encoded(64)
75 else:
76 encoded += _index_to_encoded(index)
77 encoded += _index_to_encoded(64)
78 encoded += _index_to_encoded(64)
80 return encoded
82func decode_bytes(data: bytes) -> bytes:
83 """Decode given base64 encoded data.
85 """
87 length = data.length()
89 if (length % 4) != 0:
90 raise Base64Error(f"Length {length} not multiple of 4.")
92 decoded = b""
93 decoded.reserve(3 * length / 4)
95 for i in range(0, length, 4):
96 v0 = _encoded_to_index(data[i + 0])
97 v1 = _encoded_to_index(data[i + 1])
98 v2 = _encoded_to_index(data[i + 2])
99 v3 = _encoded_to_index(data[i + 3])
101 if v0 == 64 or v1 == 64:
102 raise Base64Error(f"Bad value 64 at first of second index.")
103 elif v2 == 64:
104 decoded += (v0 << 2) | (v1 >> 4)
105 elif v3 == 64:
106 decoded += (v0 << 2) | (v1 >> 4)
107 decoded += (v1 << 4) | (v2 >> 2)
108 else:
109 decoded += (v0 << 2) | (v1 >> 4)
110 decoded += (v1 << 4) | (v2 >> 2)
111 decoded += (v2 << 6) | (v3 >> 0)
113 return decoded
115test various():
116 datas = [
117 ("", b""),
118 ("Zg==", b"f"),
119 ("Zm8=", b"fo"),
120 ("Zm9v", b"foo"),
121 ("Zm9vYg==", b"foob"),
122 ("Zm9vYmE=", b"fooba"),
123 ("Zm9vYmFy", b"foobar"),
124 (
125 "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCB"
126 "ieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaC"
127 "BpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZ"
128 "GVsaWdodCBpbiB0aGUgY29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRp"
129 "b24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW5"
130 "5IGNhcm5hbCBwbGVhc3VyZS4=",
131 b"Man is distinguished, not only by his reason, but by this singular"
132 b" passion from other animals, which is a lust of the mind, that by "
133 b"a perseverance of delight in the continued and indefatigable gener"
134 b"ation of knowledge, exceeds the short vehemence of any carnal plea"
135 b"sure."
136 ),
137 ("TQ==", b"M"),
138 ("TWE=", b"Ma"),
139 ("TWFu", b"Man")
140 ]
142 for encoded, decoded in datas:
143 assert encode(decoded) == encoded
144 assert decode(encoded) == decoded
146test various_bytes():
147 datas = [
148 (b"", b""),
149 (b"Zg==", b"f"),
150 (b"Zm8=", b"fo"),
151 (b"Zm9v", b"foo"),
152 (b"Zm9vYg==", b"foob"),
153 (b"Zm9vYmE=", b"fooba"),
154 (b"Zm9vYmFy", b"foobar"),
155 (
156 b"TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCB"
157 b"ieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaC"
158 b"BpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZ"
159 b"GVsaWdodCBpbiB0aGUgY29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRp"
160 b"b24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW5"
161 b"5IGNhcm5hbCBwbGVhc3VyZS4=",
162 b"Man is distinguished, not only by his reason, but by this singular"
163 b" passion from other animals, which is a lust of the mind, that by "
164 b"a perseverance of delight in the continued and indefatigable gener"
165 b"ation of knowledge, exceeds the short vehemence of any carnal plea"
166 b"sure."
167 ),
168 (b"TQ==", b"M"),
169 (b"TWE=", b"Ma"),
170 (b"TWFu", b"Man")
171 ]
173 for encoded, decoded in datas:
174 assert encode_bytes(decoded) == encoded
175 assert decode_bytes(encoded) == decoded
177test decode_bad_length():
178 try:
179 decode("1")
180 assert False
181 except Base64Error as error:
182 assert error.message == "Length 1 not multiple of 4."
184test decode_bad_value():
185 try:
186 decode_bytes(b"-234")
187 assert False
188 except Base64Error as error:
189 assert error.message == "Invalid value 45."
191 try:
192 decode_bytes(b"====")
193 assert False
194 except Base64Error as error:
195 assert error.message == "Bad value 64 at first of second index."