/
Base64.pir
302 lines (236 loc) · 6.83 KB
/
Base64.pir
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
=head1 NAME
MIME::Base64 - Encoding and decoding of base64 strings
=head1 SYNOPSIS
# load this library
load_bytecode 'MIME/Base64.pbc'
=head1 DESCRIPTION
MIME::Base64 is inspired by the Perl5 module MIME::Base64.
=head1 METHODS
This module defines the following subroutines:
=over 4
=item C<encode_base64( str )>
Encode data by calling the encode_base64() function. The first argument
is the string to encode.
The returned encoded string is broken into lines
of no more than 76 characters each.
=item C<decode_base64( str )>
Decode a base64 string by calling the decode_base64() function.
This function takes a single argument which is the string to decode
and returns the decoded data.
Any character not part of the 65-character base64 subset is silently ignored.
Characters occurring after a '=' padding character are never decoded.
If the length of the string to decode, after ignoring non-base64 chars,
is not a multiple of 4 or if padding occurs too early,
then a warning is generated if perl is running under -w.
=back
=cut
.include "iterator.pasm"
.namespace [ "MIME"; "Base64" ]
.sub init :load
# Base64 encoded strings are made of printable 8bit long chars,
# of which each carries 6 bit worth of information
.local string printables
printables = ascii:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
# TODO: find saner names
.local pmc six_to_eight, eight_to_six
six_to_eight = new 'FixedIntegerArray'
six_to_eight = 64 # 2 ** 6
eight_to_six = new 'FixedIntegerArray'
eight_to_six = 256 # 2 ** 8
# TODO: find easier way to initialize with undef or so
eight_to_six[0] = 0
.local int i
i = 1
START_2:
if i >= 256 goto END_2
eight_to_six[i] = -1
inc i
goto START_2
END_2:
.local int six, eight
.local string tmp
six = 0
START_1:
tmp = substr printables, six, 1
eight = ord tmp
eight_to_six[eight] = six
six_to_eight[six] = eight
inc six
if six < 64 goto START_1
set_global 'eight_to_six', eight_to_six
set_global 'six_to_eight', six_to_eight
.end
.sub encode_base64
.param string plain
.local string base64
.local pmc six_to_eight
six_to_eight = get_global 'six_to_eight'
.local int len, len_mod_3
len = length plain
.local pmc bb
bb = new ['ByteBuffer'], len
bb = plain
len = elements bb
len_mod_3 = len % 3
# Fill up with with null bytes
if len_mod_3 == 0 goto END_1
push bb, 0
if len_mod_3 == 2 goto END_1
push bb, 0
END_1:
base64 = ''
.local int i, j
.local int eight_0, eight_1, eight_2
.local int six_0, six_1, six_2, six_3
.local int tmp_int_1, tmp_int_2
.local string s_tmp_1
i = 0
j = 0
START_3:
if i >= len goto END_3
# read 3*8 bits
# TODO GH #813 and #814 unicode chars
eight_0 = bb[i]
inc i
eight_1 = bb[i]
inc i
eight_2 = bb[i]
inc i
# d[i]>>2;
shr six_0, eight_0, 2
# ((d[i]&3)<<4) | (d[i+1]>>4)
band tmp_int_1, eight_0, 3
shl tmp_int_1, 4
shr tmp_int_2, eight_1, 4
bor six_1, tmp_int_1, tmp_int_2
# ((d[i+1]&15)<<2) | (d[i+2]>>6)
band tmp_int_1, eight_1, 15
shl tmp_int_1, 2
shr tmp_int_2, eight_2, 6
bor six_2, tmp_int_1, tmp_int_2
# d[i+2]&63
band six_3, eight_2, 63
# write 4*6 bits, encoded as 4*8 bits,
# output is larger than input
tmp_int_1 = six_to_eight[six_0]
s_tmp_1 = chr tmp_int_1
base64 = concat base64, s_tmp_1
tmp_int_1 = six_to_eight[six_1]
s_tmp_1 = chr tmp_int_1
base64 = concat base64, s_tmp_1
tmp_int_1 = six_to_eight[six_2]
s_tmp_1 = chr tmp_int_1
base64 = concat base64, s_tmp_1
tmp_int_1 = six_to_eight[six_3]
s_tmp_1 = chr tmp_int_1
base64 = concat base64, s_tmp_1
inc j
if j == 19 goto line_split
goto START_3
line_split:
base64 = concat base64, "\n"
j = 0
goto START_3
END_3:
# padding with '='
if len_mod_3 == 0 goto END_2
base64 = replace base64, -1, 1, ascii:"="
if len_mod_3 == 2 goto END_2
base64 = replace base64, -2, 1, ascii:"="
END_2:
.return( base64 )
.end
.sub decode_base64
.param string base64
.local string plain, base64_cleaned
base64_cleaned = ''
plain = ''
.local pmc eight_to_six
eight_to_six = get_global 'eight_to_six'
.local int i, len
.local int tmp_int_1, tmp_int_2
.local string s_tmp_1
# Get rid of non-base64 chars
len = length base64
i = 0
START_5:
if i >= len goto END_5
tmp_int_1 = ord base64, i
inc i
tmp_int_2 = eight_to_six[tmp_int_1]
if tmp_int_2 == -1 goto START_5
s_tmp_1 = chr tmp_int_1
base64_cleaned = concat base64_cleaned, s_tmp_1
goto START_5
END_5:
.local int len_mod_4
len = length base64_cleaned
len_mod_4 = len % 4
# make sure that there are dummy bits beyond
base64_cleaned = concat base64_cleaned, ascii:"\0\0\0"
.local int eight_0, eight_1, eight_2
.local int six_0, six_1, six_2, six_3
i = 0
START_2:
if i >= len goto END_2
# read 4*6 bits
tmp_int_1 = ord base64_cleaned, i
six_0 = eight_to_six[tmp_int_1]
inc i
tmp_int_1 = ord base64_cleaned, i
six_1 = eight_to_six[tmp_int_1]
inc i
tmp_int_1 = ord base64_cleaned, i
six_2 = eight_to_six[tmp_int_1]
inc i
tmp_int_1 = ord base64_cleaned, i
six_3 = eight_to_six[tmp_int_1]
inc i
# (f64[t.charAt(i)]<<2) | (f64[t.charAt(i+1)]>>4)
shl tmp_int_1, six_0, 2
shr tmp_int_2, six_1, 4
bor eight_0, tmp_int_1, tmp_int_2
# (f64[t.charAt(i+1)]&15)<<4) | (f64[t.charAt(i+2)]>>2)
band tmp_int_1, six_1, 15
shl tmp_int_1, 4
shr tmp_int_2, six_2, 2
bor eight_1, tmp_int_1, tmp_int_2
# (f64[t.charAt(i+2)]&3)<<6) | (f64[t.charAt(i+3)])
band tmp_int_1, six_2, 3
shl tmp_int_1, 6
bor eight_2, tmp_int_1, six_3
# write 3*8 bits
# output is larger than input
s_tmp_1 = chr eight_0
plain = concat plain, s_tmp_1
s_tmp_1 = chr eight_1
plain = concat plain, s_tmp_1
s_tmp_1 = chr eight_2
plain = concat plain, s_tmp_1
goto START_2
END_2:
# cut padded '='
if len_mod_4 == 0 goto END_3
if len_mod_4 == 1 goto END_3
plain = chopn plain, 1
if len_mod_4 == 3 goto END_3
plain = chopn plain, 1
END_3:
.return( plain )
.end
=head1 SEE ALSO
L<http://aktuell.de.selfhtml.org/artikel/javascript/utf8b64/base64.htm>
L<http://en.wikipedia.org/wiki/Base64>
=head1 AUTHOR
Written and maintained by Bernhard Schmalhofer,
C<< Bernhard dot Schmalhofer at gmx dot de >>,
based on the Perl 5 Module MIME::Base64 by Gisle Aas
and on the article on de.selfhtml.org.
=head1 COPYRIGHT
Copyright (C) 2006-2012, Parrot Foundation.
=cut
# Local Variables:
# mode: pir
# fill-column: 100
# End:
# vim: expandtab shiftwidth=4 ft=pir: