1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2013 Alexis Laferrière <alexis.laf@xymus.net>
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 # Offers the base 64 encoding and decoding algorithms
22 # Alphabet used by the base64 algorithm
23 private fun base64_chars
: String
25 return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
27 private fun inverted_base64_chars
: HashMap[Char,Int]
29 var inv_base64_chars
= new HashMap[Char,Int]
30 for k
in [0..base64_chars
.length
[ do
31 inv_base64_chars
[ base64_chars
.chars
[k
] ] = k
33 return inv_base64_chars
36 # Encodes the receiver string to base64.
37 # By default, uses "=" for padding.
38 fun encode_base64
: String do return encode_base64_custom_padding
( '=' )
39 fun encode_base64_custom_padding
( padding
: Char ) : String
41 var base64_chars
= once base64_chars
44 var steps
= length
/ 3
45 var chars_in_last_step
= length
% 3
46 var result_length
= steps
*4
47 if chars_in_last_step
> 0 then result_length
+= 4
48 var result
= (padding
.to_s
*result_length
).to_cstring
52 for s
in [0..steps
[ do
55 e
+= self.chars
[s
*3+ss
].ascii
.lshift
((2-ss
)*8)
58 result
[s
*4+3-ss
] = base64_chars
.chars
[ e
.rshift
(ss
*6).bin_and
( mask_6bit
) ]
62 if chars_in_last_step
== 1 then
63 var e
= self.chars
[length-1
].ascii
.lshift
(16)
65 result
[steps
*4+1-ss
] = base64_chars
.chars
[ e
.rshift
((ss
+2)*6).bin_and
( mask_6bit
) ]
67 else if chars_in_last_step
== 2 then
68 var e
= self.chars
[length-2
].ascii
.lshift
(16) +
69 self.chars
[length-1
].ascii
.lshift
(8)
71 result
[steps
*4+2-ss
] = base64_chars
.chars
[ e
.rshift
((ss
+1)*6).bin_and
( mask_6bit
) ]
78 # Decodes the receiver string from base64.
79 # By default, uses "=" for padding.
80 fun decode_base64
: String do return decode_base64_custom_padding
( '=' )
81 fun decode_base64_custom_padding
( padding
: Char ) : String
83 var inverted_base64_chars
= once inverted_base64_chars
85 assert length
% 4 == 0 else print
"base64::decode_base64 only supports strings of length multiple of 4"
87 var steps
= length
/ 4
88 var result_length
= steps
*3
90 var padding_begin
= self.search
(padding
)
91 var padding_count
: Int
92 if padding_begin
== null then
95 padding_count
= length
- padding_begin
.from
97 result_length
-= padding_count
100 var result
= ("#"*result_length
).to_cstring
104 for s
in [0..steps
[ do
107 e
+= inverted_base64_chars
[self.chars
[s
*4+ss
]].lshift
((3-ss
)*6)
111 result
[s
*3+ss
] = e
.rshift
((2-ss
)*8).bin_and
( mask_8bit
).ascii
116 if padding_count
== 1 then
119 e
+= inverted_base64_chars
[self.chars
[s
*4+ss
]].lshift
((3-ss
)*6)
123 result
[s
*3+ss
] = e
.rshift
((2-ss
)*8).bin_and
( mask_8bit
).ascii
125 else if padding_count
== 2 then
128 e
+= inverted_base64_chars
[self.chars
[s
*4+ss
]].lshift
((3-ss
)*6)
131 result
[s
*3] = e
.rshift
(2*8).bin_and
( mask_8bit
).ascii