1
2=head1 NAME
3
4MIME::Base64 -  Encoding and decoding of base64 strings
5
6=head1 SYNOPSIS
7
8    # load this library
9    load_bytecode 'MIME/Base64.pbc'
10
11=head1 DESCRIPTION
12
13MIME::Base64 is inspired by the Perl5 module MIME::Base64.
14
15=head1 METHODS
16
17This module defines the following subroutines:
18
19=over 4
20
21=item C<encode_base64( str )>
22
23Encode data by calling the encode_base64() function. The first argument
24is the string to encode.
25The returned encoded string is broken into lines
26of no more than 76 characters each.
27
28Note: Unicode stored as MIME::Base64 is inherently endian-dependent.
29
30=item C<decode_base64( str, ?:encoding )>
31
32Decode a base64 string by calling the decode_base64() function.
33This function takes as first argument the string to decode,
34as optional second argument the encoding string for the decoded data.
35It returns the decoded data.
36
37Any character not part of the 65-character base64 subset is silently ignored.
38Characters occurring after a '=' padding character are never decoded.
39
40=back
41
42=cut
43
44.include "iterator.pasm"
45
46.namespace [ "MIME"; "Base64" ]
47
48.sub init :load
49
50    # Base64 encoded strings are made of printable 8bit long chars,
51    # of which each carries 6 bit worth of information
52    .local string printables
53    printables = ascii:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
54
55    # TODO: find saner names
56    .local pmc six_to_eight, eight_to_six
57    six_to_eight = new 'FixedIntegerArray'
58    six_to_eight = 64      # 2 ** 6
59    eight_to_six = new 'FixedIntegerArray'
60    eight_to_six = 256     # 2 ** 8
61
62    # TODO: find easier way to initialize with undef or so
63    eight_to_six[0] = 0
64    .local int i
65    i = 1
66    START_2:
67    if i >= 256 goto END_2
68        eight_to_six[i] = -1
69	inc i
70    goto START_2
71    END_2:
72
73    .local int six, eight
74    .local string tmp
75    six = 0
76    START_1:
77        tmp = substr printables, six, 1
78        eight = ord tmp
79        eight_to_six[eight] = six
80        six_to_eight[six]   = eight
81        inc six
82    if six < 64 goto START_1
83    set_global 'eight_to_six', eight_to_six
84    set_global 'six_to_eight', six_to_eight
85.end
86
87.sub encode_base64
88    .param string plain
89
90    .local string base64
91    .local pmc six_to_eight
92    six_to_eight = get_global 'six_to_eight'
93
94    .local int len, len_mod_3
95    .local pmc bb
96    # For unicode we cannot use chr/ord. This breaks endianness.
97    # GH 813 and #814
98    len = bytelength plain
99    bb = new ['ByteBuffer'], len
100    bb = plain
101
102    len_mod_3 = len % 3
103    # Fill up with with null bytes
104    if len_mod_3 == 0 goto END_1
105        push bb, 0
106        if len_mod_3 == 2 goto END_1
107            push bb, 0
108    END_1:
109
110    base64 = ''
111
112    .local int    i, j
113    .local int    eight_0, eight_1, eight_2
114    .local int    six_0, six_1, six_2, six_3
115    .local int    tmp_int_1, tmp_int_2
116    .local string s_tmp_1
117
118    i = 0
119    j = 0
120    START_3:
121    if i >= len goto END_3
122
123	# read 3*8 bits
124        eight_0 = bb[i]
125	inc i
126        eight_1 = bb[i]
127	inc i
128        eight_2 = bb[i]
129	inc i
130
131        # d[i]>>2;
132	shr six_0, eight_0, 2
133
134        # ((d[i]&3)<<4) | (d[i+1]>>4)
135	band tmp_int_1, eight_0, 3
136	shl tmp_int_1, 4
137	shr tmp_int_2, eight_1, 4
138	bor six_1, tmp_int_1, tmp_int_2
139
140        # ((d[i+1]&15)<<2) | (d[i+2]>>6)
141	band tmp_int_1, eight_1, 15
142	shl tmp_int_1, 2
143	shr tmp_int_2, eight_2, 6
144	bor six_2, tmp_int_1, tmp_int_2
145
146        # d[i+2]&63
147	band six_3, eight_2, 63
148
149	# write 4*6 bits, encoded as 4*8 bits,
150	# output is larger than input
151	tmp_int_1 = six_to_eight[six_0]
152	s_tmp_1 = chr tmp_int_1
153	base64 = concat base64, s_tmp_1
154	tmp_int_1 = six_to_eight[six_1]
155	s_tmp_1 = chr tmp_int_1
156	base64 = concat base64, s_tmp_1
157	tmp_int_1 = six_to_eight[six_2]
158	s_tmp_1 = chr tmp_int_1
159	base64 = concat base64, s_tmp_1
160	tmp_int_1 = six_to_eight[six_3]
161	s_tmp_1 = chr tmp_int_1
162	base64 = concat base64, s_tmp_1
163	inc j
164
165	if j == 19 goto line_split
166	goto START_3
167      line_split:
168        base64 = concat base64, "\n"
169        j = 0
170        goto START_3
171    END_3:
172
173    # padding with '='
174    if len_mod_3 == 0 goto END_2
175        base64 = replace base64, -1, 1, ascii:"="
176        if len_mod_3 == 2 goto END_2
177            base64 = replace base64, -2, 1, ascii:"="
178    END_2:
179
180    .return( base64 )
181.end
182
183.sub decode_base64
184    .param string base64
185    .param string enc     :optional
186    .param int    has_enc :opt_flag
187
188    .local string result, base64_cleaned
189    .local int    enc_num
190    base64_cleaned = ''
191    if has_enc goto HAS_ENC
192      enc = 'ascii'
193  HAS_ENC:
194
195    .local pmc eight_to_six, bb
196    eight_to_six = get_global 'eight_to_six'
197
198    .local int    i, len
199    .local int    tmp_int_1, tmp_int_2
200
201    # Get rid of non-base64 chars
202    len = length base64
203    i = 0
204  START_5:
205    .local string s_tmp_1
206    if i >= len goto END_5
207        tmp_int_1 = ord base64, i
208        inc i
209        tmp_int_2 = eight_to_six[tmp_int_1]
210        if tmp_int_2 == -1 goto START_5
211        s_tmp_1 = chr tmp_int_1
212        base64_cleaned = concat base64_cleaned, s_tmp_1
213    goto START_5
214
215  END_5:
216    .local int len_mod_4
217    len = length base64_cleaned
218    len_mod_4 = len % 4
219
220    # make sure that there are dummy bits beyond
221    base64_cleaned = concat base64_cleaned, ascii:"\0\0\0"
222
223    bb = new ['ByteBuffer']
224    .local int    eight_0, eight_1, eight_2
225    .local int    six_0, six_1, six_2, six_3
226
227    i = 0
228  START_2:
229    if i >= len goto END_2
230
231	# read 4*6 bits
232        tmp_int_1 = ord base64_cleaned, i
233        six_0 = eight_to_six[tmp_int_1]
234        inc i
235        tmp_int_1 = ord base64_cleaned, i
236        six_1 = eight_to_six[tmp_int_1]
237        inc i
238        tmp_int_1 = ord base64_cleaned, i
239        six_2 = eight_to_six[tmp_int_1]
240        inc i
241        tmp_int_1 = ord base64_cleaned, i
242        six_3 = eight_to_six[tmp_int_1]
243        inc i
244
245        # (f64[t.charAt(i)]<<2) | (f64[t.charAt(i+1)]>>4)
246        shl tmp_int_1, six_0, 2
247	shr tmp_int_2, six_1, 4
248	bor eight_0, tmp_int_1, tmp_int_2
249
250        # (f64[t.charAt(i+1)]&15)<<4) | (f64[t.charAt(i+2)]>>2)
251        band tmp_int_1, six_1, 15
252	shl tmp_int_1, 4
253	shr tmp_int_2, six_2, 2
254	bor eight_1, tmp_int_1, tmp_int_2
255
256        # (f64[t.charAt(i+2)]&3)<<6) | (f64[t.charAt(i+3)])
257        band tmp_int_1, six_2, 3
258	shl tmp_int_1, 6
259	bor eight_2, tmp_int_1, six_3
260
261	# write 3*8 bits
262	# output is larger than input
263        push bb, eight_0
264        push bb, eight_1
265        push bb, eight_2
266    goto START_2
267
268  END_2:
269    # cut padded '='
270    if len_mod_4 == 0 goto END_3
271        if len_mod_4 == 1 goto END_3
272            len = elements bb
273	    dec len
274	    bb = len
275            if len_mod_4 == 3 goto END_3
276	        dec len
277	        bb = len
278
279  END_3:
280    result = bb.'get_string'(enc)
281    .return( result )
282.end
283
284=head1 SEE ALSO
285
286L<http://aktuell.de.selfhtml.org/artikel/javascript/utf8b64/base64.htm>
287L<http://en.wikipedia.org/wiki/Base64>
288
289=head1 AUTHOR
290
291Written and maintained by Bernhard Schmalhofer,
292C<< Bernhard dot Schmalhofer at gmx dot de >>,
293based on the Perl 5 Module MIME::Base64 by Gisle Aas
294and on the article on de.selfhtml.org.
295
296=head1 COPYRIGHT
297
298Copyright (C) 2006-2012, Parrot Foundation.
299
300=cut
301
302# Local Variables:
303#   mode: pir
304#   fill-column: 100
305# End:
306# vim: expandtab shiftwidth=4 ft=pir:
307