1
/*
2
 * Hurl (https://hurl.dev)
3
 * Copyright (C) 2025 Orange
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
 */
18
use crate::reader::Reader;
19

            
20
/// <https://en.wikipedia.org/wiki/Base64>
21
/// Test padding/no-padding
22
///
23
/// Encoded
24
/// YW55IGNhcm5hbCBwbGVhcw==    any carnal pleas   # [97, 110, 121, 32, 99, 97, 114, 110, 97, 108, 32, 112, 108, 101, 97, 115]
25
345
pub fn parse(reader: &mut Reader) -> Vec<u8> {
26
345
    let mut bytes = vec![];
27
345
    let mut buf = vec![]; // base64 text
28
    loop {
29
7100
        let pad = padding(reader);
30
7100
        if !pad.is_empty() {
31
85
            break;
32
        }
33
7015
        let save = reader.cursor();
34
7015
        match reader.read() {
35
            None => {
36
                break;
37
            }
38
            Some(' ') | Some('\n') | Some('\t') => {}
39
7015
            Some(c) => match value(c) {
40
                None => {
41
260
                    reader.seek(save);
42
260
                    break;
43
                }
44
6755
                Some(v) => {
45
6755
                    buf.push(v);
46
6755
                    if buf.len() == 4 {
47
1655
                        let bs = decode_four_chars(
48
1655
                            *buf.first().unwrap(),
49
1655
                            *buf.get(1).unwrap(),
50
1655
                            *buf.get(2).unwrap(),
51
1655
                            *buf.get(3).unwrap(),
52
                        );
53
6620
                        for b in bs {
54
4965
                            bytes.push(b);
55
                        }
56
1655
                        buf = vec![];
57
                    }
58
                }
59
            },
60
        }
61
    }
62
345
    match buf.as_slice() {
63
65
        [c1, c2] => bytes.append(&mut decode_two_chars(*c1, *c2)),
64
        [c1, c2, c3] => bytes.append(&mut decode_three_chars(*c1, *c2, *c3)),
65
280
        _ => {}
66
    }
67
345
    bytes
68
}
69

            
70
7015
fn value(c: char) -> Option<i32> {
71
7015
    match c {
72
60
        'A' => Some(0),
73
20
        'B' => Some(1),
74
150
        'C' => Some(2),
75
60
        'D' => Some(3),
76
105
        'E' => Some(4),
77
20
        'F' => Some(5),
78
795
        'G' => Some(6),
79
20
        'H' => Some(7),
80
145
        'I' => Some(8),
81
20
        'J' => Some(9),
82
145
        'K' => Some(10),
83
20
        'L' => Some(11),
84
40
        'M' => Some(12),
85
85
        'N' => Some(13),
86
20
        'O' => Some(14),
87
20
        'P' => Some(15),
88
255
        'Q' => Some(16),
89
20
        'R' => Some(17),
90
215
        'S' => Some(18),
91
210
        'T' => Some(19),
92
85
        'U' => Some(20),
93
410
        'V' => Some(21),
94
20
        'W' => Some(22),
95
20
        'X' => Some(23),
96
20
        'Y' => Some(24),
97
210
        'Z' => Some(25),
98
45
        'a' => Some(26),
99
665
        'b' => Some(27),
100
20
        'c' => Some(28),
101
20
        'd' => Some(29),
102
20
        'e' => Some(30),
103
20
        'f' => Some(31),
104
280
        'g' => Some(32),
105
215
        'h' => Some(33),
106
20
        'i' => Some(34),
107
20
        'j' => Some(35),
108
20
        'k' => Some(36),
109
210
        'l' => Some(37),
110
150
        'm' => Some(38),
111
20
        'n' => Some(39),
112
20
        'o' => Some(40),
113
85
        'p' => Some(41),
114
20
        'q' => Some(42),
115
20
        'r' => Some(43),
116
215
        's' => Some(44),
117
20
        't' => Some(45),
118
210
        'u' => Some(46),
119
20
        'v' => Some(47),
120
20
        'w' => Some(48),
121
85
        'x' => Some(49),
122
215
        'y' => Some(50),
123
85
        'z' => Some(51),
124
20
        '0' => Some(52),
125
20
        '1' => Some(53),
126
215
        '2' => Some(54),
127
20
        '3' => Some(55),
128
20
        '4' => Some(56),
129
20
        '5' => Some(57),
130
20
        '6' => Some(58),
131
20
        '7' => Some(59),
132
215
        '8' => Some(60),
133
215
        '9' => Some(61),
134
20
        '+' => Some(62),
135
20
        '/' => Some(63),
136
260
        _ => None,
137
    }
138
}
139

            
140
7100
fn padding(reader: &mut Reader) -> String {
141
    // consume padding can not fail
142
7100
    let mut buf = String::new();
143
    loop {
144
7270
        let save = reader.cursor();
145
7270
        match reader.read() {
146
170
            Some('=') => {
147
170
                buf.push('=');
148
            }
149
            _ => {
150
7100
                reader.seek(save);
151
7100
                break;
152
            }
153
        }
154
    }
155
7100
    buf
156
}
157

            
158
65
fn decode_two_chars(c1: i32, c2: i32) -> Vec<u8> {
159
65
    vec![(((c1 << 2) & 255) + (c2 >> 4)) as u8]
160
}
161

            
162
fn decode_three_chars(c1: i32, c2: i32, c3: i32) -> Vec<u8> {
163
    vec![
164
        (((c1 << 2) & 255) + (c2 >> 4)) as u8,
165
        (((c2 << 4) & 255) + (c3 >> 2)) as u8,
166
    ]
167
}
168

            
169
1655
fn decode_four_chars(c1: i32, c2: i32, c3: i32, c4: i32) -> Vec<u8> {
170
1655
    vec![
171
1655
        (((c1 << 2) & 255) + (c2 >> 4)) as u8,
172
1655
        (((c2 << 4) & 255) + (c3 >> 2)) as u8,
173
1655
        (((c3 << 6) & 255) + c4) as u8,
174
    ]
175
}
176

            
177
#[cfg(test)]
178
mod tests {
179
    use super::*;
180
    use crate::reader::CharPos;
181

            
182
    #[test]
183
    fn test_decode_one_block() {
184
        let mut reader = Reader::new("");
185
        assert_eq!(parse(&mut reader), vec![] as Vec<u8>);
186
        assert_eq!(reader.cursor().index, CharPos(0));
187

            
188
        let mut reader = Reader::new("AA==;");
189
        assert_eq!(parse(&mut reader), vec![0]);
190
        assert_eq!(reader.cursor().index, CharPos(4));
191

            
192
        let mut reader = Reader::new("AA");
193
        assert_eq!(parse(&mut reader), vec![0]);
194
        assert_eq!(reader.cursor().index, CharPos(2));
195

            
196
        let mut reader = Reader::new("AA;");
197
        assert_eq!(parse(&mut reader), vec![0]);
198
        assert_eq!(reader.cursor().index, CharPos(2));
199

            
200
        let mut reader = Reader::new("TWE=;");
201
        assert_eq!(parse(&mut reader), vec![77, 97]);
202
        assert_eq!(reader.cursor().index, CharPos(4));
203

            
204
        let mut reader = Reader::new("TWFu;");
205
        assert_eq!(parse(&mut reader), vec![77, 97, 110]);
206
        assert_eq!(reader.cursor().index, CharPos(4));
207
    }
208

            
209
    /*
210
    |   Y       |     W     |     5     |     5     |
211
    |     24    |    22     |      57   |     57    |
212
    |0|1|1|0|0|0|0|1|0|1|1|0|1|1|1|0|0|1|1|1|1|0|0|1|
213
    |      97       |     110       |      121      |
214
    */
215

            
216
    /*
217
    |   Y       |     W     |     5     |     5     |
218
    |     24    |    22     |      57   |     57    |
219
    |0|1|1|0|0|0|0|1|0|1|1|0|1|1|1|0|0|1|1|1|1|0|0|1|
220
    |      97       |     110       |      121      |
221
    */
222

            
223
    #[test]
224
    fn test_decode_with_padding() {
225
        let mut reader = Reader::new("YW55IGNhcm5hbCBwbGVhcw==;");
226
        let decoded = parse(&mut reader);
227
        assert_eq!(decoded, b"any carnal pleas");
228

            
229
        let mut reader = Reader::new("YW55IGNhcm5hbCBwbGVhc3U=;");
230
        assert_eq!(parse(&mut reader), b"any carnal pleasu");
231

            
232
        let mut reader = Reader::new("YW55IGNhcm5hbCBwbGVhc3Vy;");
233
        assert_eq!(parse(&mut reader), b"any carnal pleasur");
234
    }
235

            
236
    #[test]
237
    fn test_decode_without_padding() {
238
        let mut reader = Reader::new("YW55IGNhcm5hbCBwbGVhcw;");
239
        assert_eq!(parse(&mut reader), b"any carnal pleas");
240

            
241
        let mut reader = Reader::new("YW55IGNhcm5hbCBwbGVhc3U;");
242
        assert_eq!(parse(&mut reader), b"any carnal pleasu");
243
    }
244

            
245
    #[test]
246
    fn test_decode_with_whitespace() {
247
        let mut reader = Reader::new("TW E=\n;");
248
        assert_eq!(parse(&mut reader), vec![77, 97]);
249
        assert_eq!(reader.cursor().index, CharPos(5));
250
    }
251

            
252
    #[test]
253
    fn test_decode_two_chars() {
254
        assert_eq!(
255
            decode_two_chars(value('A').unwrap(), value('A').unwrap()),
256
            vec![0]
257
        );
258
        assert_eq!(
259
            decode_two_chars(value('A').unwrap(), value('Q').unwrap()),
260
            vec![1]
261
        );
262
        assert_eq!(
263
            decode_two_chars(value('T').unwrap(), value('Q').unwrap()),
264
            vec![77]
265
        );
266
    }
267

            
268
    #[test]
269
    fn test_decode_three_chars() {
270
        assert_eq!(
271
            decode_three_chars(
272
                value('T').unwrap(),
273
                value('W').unwrap(),
274
                value('E').unwrap(),
275
            ),
276
            vec![77, 97]
277
        );
278
    }
279

            
280
    #[test]
281
    fn test_decode_four_chars() {
282
        assert_eq!(
283
            decode_four_chars(
284
                value('Y').unwrap(),
285
                value('W').unwrap(),
286
                value('5').unwrap(),
287
                value('5').unwrap(),
288
            ),
289
            vec![97, 110, 121]
290
        );
291
        assert_eq!(
292
            decode_four_chars(
293
                value('T').unwrap(),
294
                value('W').unwrap(),
295
                value('F').unwrap(),
296
                value('u').unwrap(),
297
            ),
298
            vec![77, 97, 110]
299
        );
300
    }
301
}