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[Byte, Byte]
29 var inv_base64_chars
= new HashMap[Byte, Byte]
30 for k
in [0..base64_chars
.bytelen
[ do
31 inv_base64_chars
[base64_chars
.bytes
[k
]] = k
.to_b
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
('='.ascii
.to_b
)
40 # Encodes the receiver string to base64 using a custom padding character.
42 # If using the default padding character `=`, see `encode_base64`.
43 fun encode_base64_custom_padding
(padding
: Byte) : String
45 var base64_bytes
= once base64_chars
.bytes
48 var steps
= length
/ 3
49 var bytes_in_last_step
= length
% 3
50 var result_length
= steps
* 4
51 if bytes_in_last_step
> 0 then result_length
+= 4
52 var result
= new NativeString(result_length
+ 1)
53 var bytes
= self.bytes
54 result
[result_length
] = 0u8
56 var mask_6bit
= 0b0011_1111
58 for s
in [0 .. steps
[ do
61 e
+= bytes
[s
* 3 + ss
].to_i
<< ((2 - ss
) * 8)
64 result
[s
* 4 + 3 - ss
] = base64_bytes
[(e
>> (ss
* 6)) & mask_6bit
]
68 var out_off
= result_length
- 4
69 var in_off
= length
- bytes_in_last_step
70 if bytes_in_last_step
== 1 then
71 result
[out_off
] = base64_bytes
[((bytes
[in_off
] & 0b1111_1100u
8) >> 2).to_i
]
72 result
[out_off
+ 1] = base64_bytes
[((bytes
[in_off
] & 0b0000_0011u
8) << 4).to_i
]
74 else if bytes_in_last_step
== 2 then
75 result
[out_off
] = base64_bytes
[((bytes
[in_off
] & 0b1111_1100u
8) >> 2).to_i
]
76 result
[out_off
+ 1] = base64_bytes
[(((bytes
[in_off
] & 0b0000_0011u
8) << 4) | ((bytes
[in_off
+ 1] & 0b1111_0000u
8) >> 4)).to_i
]
77 result
[out_off
+ 2] = base64_bytes
[((bytes
[in_off
+ 1] & 0b0000_1111u
8) << 2).to_i
]
80 if bytes_in_last_step
> 0 then
81 for i
in [out_off
.. result_length
[ do result
[i
] = padding
84 return result
.to_s_with_length
(result_length
)
87 # Decodes the receiver string from base64.
88 # By default, uses "=" for padding.
89 fun decode_base64
: String do return decode_base64_custom_padding
('='.ascii
.to_b
)
91 # Decodes the receiver string to base64 using a custom padding character.
93 # If using the default padding character `=`, see `decode_base64`.
94 fun decode_base64_custom_padding
(padding
: Byte) : String
96 var inv
= once inverted_base64_chars
98 if length
== 0 then return ""
99 assert length
% 4 == 0 else print
"base64::decode_base64 only supports strings of length multiple of 4"
101 var bytes
= self.bytes
102 var steps
= length
/ 4
103 var result_length
= steps
* 3
105 var epos
= length
- 1
107 while epos
>= 0 and bytes
[epos
] == padding
do
112 if padding_len
!= 0 then steps
-= 1
113 if padding_len
== 1 then result_length
-= 1
114 if padding_len
== 2 then result_length
-= 2
116 var result
= new NativeString(result_length
+ 1)
117 result
[result_length
] = 0u8
119 for s
in [0 .. steps
[ do
120 var c0
= inv
[bytes
[s
* 4]]
121 var c1
= inv
[bytes
[s
* 4 + 1]]
122 var c2
= inv
[bytes
[s
* 4 + 2]]
123 var c3
= inv
[bytes
[s
* 4 + 3]]
124 result
[s
* 3] = ((c0
& 0b0011_1111u
8) << 2) | ((c1
& 0b0011_0000u
8) >> 4)
125 result
[s
* 3 + 1] = ((c1
& 0b0000_1111u
8) << 4) | ((c2
& 0b0011_1100u
8) >> 2)
126 result
[s
* 3 + 2] = ((c2
& 0b0000_0011u
8) << 6) | (c3
& 0b0011_1111u
8)
129 var last_start
= steps
* 4
130 if padding_len
== 1 then
131 var c0
= inv
[bytes
[last_start
]]
132 var c1
= inv
[bytes
[last_start
+ 1]]
133 var c2
= inv
[bytes
[last_start
+ 2]]
134 result
[result_length
- 2] = ((c0
& 0b0011_1111u
8) << 2) | ((c1
& 0b0011_0000u
8) >> 4)
135 result
[result_length
- 1] = ((c1
& 0b0000_1111u
8) << 4) | ((c2
& 0b0011_1100u
8) >> 2)
136 else if padding_len
== 2 then
137 var c0
= inv
[bytes
[last_start
]]
138 var c1
= inv
[bytes
[last_start
+ 1]]
139 result
[result_length
- 1] = ((c0
& 0b0011_1111u
8) << 2) | ((c1
& 0b0011_0000u
8) >> 4)
142 return result
.to_s_with_length
(result_length
)