projects: update some short descriptions
[nit.git] / lib / base64.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2013 Alexis Laferrière <alexis.laf@xymus.net>
4 #
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
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
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.
16
17 # Offers the base 64 encoding and decoding algorithms
18 module base64
19
20 redef class String
21
22 # Alphabet used by the base64 algorithm
23 private fun base64_chars : String
24 do
25 return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
26 end
27 private fun inverted_base64_chars : HashMap[Byte, Byte]
28 do
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
32 end
33 return inv_base64_chars
34 end
35
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)
39
40 # Encodes the receiver string to base64 using a custom padding character.
41 #
42 # If using the default padding character `=`, see `encode_base64`.
43 fun encode_base64_custom_padding(padding : Byte) : String
44 do
45 var base64_bytes = once base64_chars.bytes
46 var length = bytelen
47
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
55
56 var mask_6bit = 0b0011_1111
57
58 for s in [0 .. steps[ do
59 var e = 0
60 for ss in [0 .. 3[ do
61 e += bytes[s * 3 + ss].to_i << ((2 - ss) * 8)
62 end
63 for ss in [0..4[ do
64 result[s * 4 + 3 - ss] = base64_bytes[(e >> (ss * 6)) & mask_6bit]
65 end
66 end
67
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_1100u8) >> 2).to_i]
72 result[out_off + 1] = base64_bytes[((bytes[in_off] & 0b0000_0011u8) << 4).to_i]
73 out_off += 2
74 else if bytes_in_last_step == 2 then
75 result[out_off] = base64_bytes[((bytes[in_off] & 0b1111_1100u8) >> 2).to_i]
76 result[out_off + 1] = base64_bytes[(((bytes[in_off] & 0b0000_0011u8) << 4) | ((bytes[in_off + 1] & 0b1111_0000u8) >> 4)).to_i]
77 result[out_off + 2] = base64_bytes[((bytes[in_off + 1] & 0b0000_1111u8) << 2).to_i]
78 out_off += 3
79 end
80 if bytes_in_last_step > 0 then
81 for i in [out_off .. result_length[ do result[i] = padding
82 end
83
84 return result.to_s_with_length(result_length)
85 end
86
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)
90
91 # Decodes the receiver string to base64 using a custom padding character.
92 #
93 # If using the default padding character `=`, see `decode_base64`.
94 fun decode_base64_custom_padding(padding : Byte) : String
95 do
96 var inv = once inverted_base64_chars
97 var length = bytelen
98 if length == 0 then return ""
99 assert length % 4 == 0 else print "base64::decode_base64 only supports strings of length multiple of 4"
100
101 var bytes = self.bytes
102 var steps = length / 4
103 var result_length = steps * 3
104
105 var epos = length - 1
106 var padding_len = 0
107 while epos >= 0 and bytes[epos] == padding do
108 epos -= 1
109 padding_len += 1
110 end
111
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
115
116 var result = new NativeString(result_length + 1)
117 result[result_length] = 0u8
118
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_1111u8) << 2) | ((c1 & 0b0011_0000u8) >> 4)
125 result[s * 3 + 1] = ((c1 & 0b0000_1111u8) << 4) | ((c2 & 0b0011_1100u8) >> 2)
126 result[s * 3 + 2] = ((c2 & 0b0000_0011u8) << 6) | (c3 & 0b0011_1111u8)
127 end
128
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_1111u8) << 2) | ((c1 & 0b0011_0000u8) >> 4)
135 result[result_length - 1] = ((c1 & 0b0000_1111u8) << 4) | ((c2 & 0b0011_1100u8) >> 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_1111u8) << 2) | ((c1 & 0b0011_0000u8) >> 4)
140 end
141
142 return result.to_s_with_length(result_length)
143 end
144 end