v / vlib / encoding / base64 / base64.c.v
247 lines · 218 sloc · 7.6 KB · e2e5cf8db56f3562c7baa735061690be936bdf3e
Raw
1module base64
2
3// encode_in_buffer base64 encodes the `[]u8` passed in `data` into `buffer`.
4// encode_in_buffer returns the size of the encoded data in the buffer.
5// Please note: The buffer should be large enough (i.e. 4/3 of the data.len, or larger) to hold the encoded data.
6// Please note: The function does NOT allocate new memory, and is suitable for handling very large strings.
7pub fn encode_in_buffer(data []u8, buffer &u8) int {
8 return encode_from_buffer(buffer, data.data, data.len)
9}
10
11// encode_from_buffer will perform encoding from any type of src buffer
12// and write the bytes into `dest`.
13// Please note: The `dest` buffer should be large enough (i.e. 4/3 of the src_len, or larger) to hold the encoded data.
14// Please note: This function is for internal base64 encoding
15fn encode_from_buffer(dest &u8, src &u8, src_len int) int {
16 if src_len == 0 {
17 return 0
18 }
19 output_length := 4 * ((src_len + 2) / 3)
20
21 mut d := unsafe { src }
22 mut b := unsafe { dest }
23 etable := enc_table.str
24
25 mut di := 0
26 mut si := 0
27 n := (src_len / 3) * 3
28 for si < n {
29 // Convert 3x 8bit source bytes into 4 bytes
30 unsafe {
31 val := u32(d[si + 0]) << 16 | u32(d[si + 1]) << 8 | u32(d[si + 2])
32
33 b[di + 0] = etable[(val >> 18) & 0x3F]
34 b[di + 1] = etable[(val >> 12) & 0x3F]
35 b[di + 2] = etable[(val >> 6) & 0x3F]
36 b[di + 3] = etable[val & 0x3F]
37 }
38 si += 3
39 di += 4
40 }
41
42 remain := src_len - si
43 if remain == 0 {
44 return output_length
45 }
46
47 // Add the remaining small block and padding
48 unsafe {
49 mut val := u32(d[si + 0]) << 16
50 if remain == 2 {
51 val |= u32(d[si + 1]) << 8
52 }
53
54 b[di + 0] = etable[(val >> 18) & 0x3F]
55 b[di + 1] = etable[(val >> 12) & 0x3F]
56
57 match remain {
58 2 {
59 b[di + 2] = etable[(val >> 6) & 0x3F]
60 b[di + 3] = u8(`=`)
61 }
62 1 {
63 b[di + 2] = u8(`=`)
64 b[di + 3] = u8(`=`)
65 }
66 else {
67 panic('base64: This case should never occur.')
68 }
69 }
70 }
71 return output_length
72}
73
74// decode_in_buffer decodes the base64 encoded `string` reference passed in `data` into `buffer`.
75// decode_in_buffer returns the size of the decoded data in the buffer.
76// Please note: The `buffer` should be large enough (i.e. 3/4 of the data.len, or larger)
77// to hold the decoded data.
78// Please note: This function does NOT allocate new memory, and is thus suitable for handling very large strings.
79pub fn decode_in_buffer(data &string, buffer &u8) int {
80 return decode_from_buffer(buffer, data.str, data.len)
81}
82
83// decode_from_buffer decodes the base64 encoded ASCII bytes from `data` into `buffer`.
84// decode_from_buffer returns the size of the decoded data in the buffer.
85// Please note: The `buffer` should be large enough (i.e. 3/4 of the data.len, or larger)
86// to hold the decoded data.
87// Please note: This function does NOT allocate new memory, and is thus suitable for handling very large strings.
88pub fn decode_in_buffer_bytes(data []u8, buffer &u8) int {
89 return decode_from_buffer(buffer, data.data, data.len)
90}
91
92// decode_from_buffer decodes the base64 encoded ASCII bytes from `src` into `dest`.
93// decode_from_buffer returns the size of the decoded data in the buffer.
94// Please note: The `dest` buffer should be large enough (i.e. 3/4 of the `src_len`, or larger)
95// to hold the decoded data.
96// Please note: This function does NOT allocate new memory, and is thus suitable for handling very large strings.
97// Please note: This function is for internal base64 decoding
98fn decode_from_buffer(dest &u8, src &u8, src_len int) int {
99 if src_len < 4 {
100 return 0
101 }
102
103 mut padding := 0
104 if unsafe { src[src_len - 1] == `=` } {
105 if unsafe { src[src_len - 2] == `=` } {
106 padding = 2
107 } else {
108 padding = 1
109 }
110 }
111
112 mut d := unsafe { src }
113 mut b := unsafe { dest }
114
115 unsafe {
116 mut n_decoded_bytes := 0 // padding bytes are also counted towards this.
117 mut si := 0
118
119 mut datablock_64 := B64_64_datablock{
120 data: 0
121 }
122 mut datablock_32 := B64_32_datablock{
123 data: 0
124 }
125
126 for src_len - si >= 8 {
127 // Converting 8 bytes of input into 6 bytes of output. Storing these in the upper bytes of an u64.
128 datablock_64.data = assemble64(u8(index[d[si + 0]]), u8(index[d[si + 1]]), u8(index[d[
129 si + 2]]), u8(index[d[si + 3]]), u8(index[d[si + 4]]), u8(index[d[si + 5]]), u8(index[d[
130 si + 6]]), u8(index[d[si + 7]]))
131
132 // Reading out the individual bytes from the u64. Watch out with endianness.
133 $if little_endian {
134 b[n_decoded_bytes + 0] = datablock_64.data_byte[7]
135 b[n_decoded_bytes + 1] = datablock_64.data_byte[6]
136 b[n_decoded_bytes + 2] = datablock_64.data_byte[5]
137 b[n_decoded_bytes + 3] = datablock_64.data_byte[4]
138 b[n_decoded_bytes + 4] = datablock_64.data_byte[3]
139 b[n_decoded_bytes + 5] = datablock_64.data_byte[2]
140 } $else {
141 b[n_decoded_bytes + 0] = datablock_64.data_byte[0]
142 b[n_decoded_bytes + 1] = datablock_64.data_byte[1]
143 b[n_decoded_bytes + 2] = datablock_64.data_byte[2]
144 b[n_decoded_bytes + 3] = datablock_64.data_byte[3]
145 b[n_decoded_bytes + 4] = datablock_64.data_byte[4]
146 b[n_decoded_bytes + 5] = datablock_64.data_byte[5]
147 }
148
149 n_decoded_bytes += 6
150 si += 8
151 }
152
153 for src_len - si >= 4 {
154 datablock_32.data = assemble32(u8(index[d[si + 0]]), u8(index[d[si + 1]]), u8(index[d[
155 si + 2]]), u8(index[d[si + 3]]))
156 $if little_endian {
157 b[n_decoded_bytes + 0] = datablock_32.data_byte[3]
158 b[n_decoded_bytes + 1] = datablock_32.data_byte[2]
159 b[n_decoded_bytes + 2] = datablock_32.data_byte[1]
160 b[n_decoded_bytes + 3] = datablock_32.data_byte[0]
161 } $else {
162 b[n_decoded_bytes + 0] = datablock_32.data_byte[0]
163 b[n_decoded_bytes + 1] = datablock_32.data_byte[1]
164 b[n_decoded_bytes + 2] = datablock_32.data_byte[2]
165 b[n_decoded_bytes + 3] = datablock_32.data_byte[3]
166 }
167
168 n_decoded_bytes += 3
169 si += 4
170 }
171
172 return n_decoded_bytes - padding
173 }
174}
175
176union B64_64_datablock {
177mut:
178 data u64
179 data_byte [8]u8
180}
181
182union B64_32_datablock {
183mut:
184 data u32
185 data_byte [4]u8
186}
187
188// decode decodes the base64 encoded `string` value passed in `data`.
189// Please note: If you need to decode many strings repeatedly, take a look at `decode_in_buffer`.
190// Example: assert base64.decode('ViBpbiBiYXNlIDY0') == 'V in base 64'.bytes()
191pub fn decode(data string) []u8 {
192 mut size := i64(data.len) * 3 / 4
193 if size <= 0 || data.len % 4 != 0 {
194 return []
195 }
196 size = (size + 3) & ~0x03 // round to the next multiple of 4 (the decoding loop writes multiples of 4 bytes)
197 unsafe {
198 buffer := malloc(int(size))
199 n := decode_in_buffer(data, buffer)
200 return buffer.vbytes(n)
201 }
202}
203
204// decode_str is the string variant of decode
205pub fn decode_str(data string) string {
206 size := data.len * 3 / 4
207 if size <= 0 || data.len % 4 != 0 {
208 return ''
209 }
210 unsafe {
211 buffer := malloc_noscan(size + 1)
212 buffer[size] = 0
213 blen := decode_in_buffer(data, buffer)
214 return tos(buffer, blen)
215 }
216}
217
218// encode encodes the `[]u8` value passed in `data` to base64.
219// Please note: base64 encoding returns a `string` that is ~ 4/3 larger than the input.
220// Please note: If you need to encode many strings repeatedly, take a look at `encode_in_buffer`.
221// Example: assert base64.encode('V in base 64'.bytes()) == 'ViBpbiBiYXNlIDY0'
222pub fn encode(data []u8) string {
223 return alloc_and_encode(data.data, data.len)
224}
225
226// encode_str is the string variant of encode
227pub fn encode_str(data string) string {
228 return alloc_and_encode(data.str, data.len)
229}
230
231// alloc_and_encode is a private function that allocates and encodes data into a string
232// Used by encode and encode_str
233fn alloc_and_encode(src &u8, len int) string {
234 if len == 0 {
235 return ''
236 }
237 size := 4 * ((len + 2) / 3)
238 if size <= 0 {
239 return ''
240 }
241 unsafe {
242 buffer := malloc_noscan(size + 1)
243 buffer[size] = 0
244 blen := encode_from_buffer(buffer, src, len)
245 return tos(buffer, blen)
246 }
247}
248