1
use crate::parser::types::{Authority, Host, IpAddr, Uri};
2
use crate::parser::{Error, InnerError};
3
use crate::single;
4
use nom::branch::alt;
5
use nom::bytes::streaming::{tag, take_while, take_while1, take_while_m_n};
6
use nom::character::streaming::char;
7
use nom::combinator::{map_res, opt, recognize, verify};
8
use nom::error::ParseError;
9
use nom::multi::{fold_many0, fold_many1, many0, many1, separated_list0};
10
use nom::{AsChar, IResult, Parser};
11
use std::fmt::{Display, Formatter, Write};
12
use std::net::{Ipv4Addr, Ipv6Addr};
13

            
14
// TODO can be a property or a param value, rename
15
542
pub fn param_value_uri<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], Uri<'a>, E>
16
542
where
17
542
    E: ParseError<&'a [u8]>
18
542
        + nom::error::FromExternalError<&'a [u8], nom::Err<E>>
19
542
        + From<Error<'a>>,
20
542
{
21
542
    let (input, (scheme, _, (authority, path), query, fragment)) = (
22
542
        scheme,
23
542
        char(':'),
24
542
        alt((
25
542
            (tag("//"), authority, opt(path_absolute_empty)).map(|(_, a, b)| (Some(a), b)),
26
542
            path_absolute.map(|p| (None, Some(p))),
27
542
            path_rootless.map(|p| (None, Some(p))),
28
542
        )),
29
542
        opt((char('?'), query_or_fragment).map(|(_, v)| v)),
30
542
        opt((char('#'), query_or_fragment).map(|(_, v)| v)),
31
542
    )
32
542
        .parse(input)?;
33

            
34
538
    Ok((
35
538
        input,
36
538
        Uri {
37
538
            scheme,
38
538
            authority,
39
538
            path: path.unwrap_or_default().to_vec(),
40
538
            query,
41
538
            fragment,
42
538
        },
43
538
    ))
44
542
}
45

            
46
#[inline]
47
3026
const fn is_scheme_char(b: u8) -> bool {
48
3026
    matches!(b, b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'+' | b'-' | b'.')
49
3026
}
50

            
51
542
fn scheme<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], &'a [u8], E>
52
542
where
53
542
    E: ParseError<&'a [u8]> + From<Error<'a>>,
54
542
{
55
542
    verify(take_while1(is_scheme_char), |sch: &[u8]| {
56
542
        AsChar::is_alpha(sch[0])
57
542
    })
58
542
    .parse(input)
59
542
}
60

            
61
#[inline]
62
24
const fn is_hex_digit_upper(b: u8) -> bool {
63
24
    matches!(b, b'0'..=b'9' | b'A'..=b'F')
64
24
}
65

            
66
#[inline]
67
44
const fn is_hex_digit(b: u8) -> bool {
68
44
    b.is_ascii_hexdigit()
69
44
}
70

            
71
#[inline]
72
13412
const fn is_unreserved(b: u8) -> bool {
73
13412
    matches!(b, b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'.' | b'_' | b'~')
74
12456
}
75

            
76
1372
fn pct_encoded<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], Vec<u8>, E>
77
1372
where
78
1372
    E: ParseError<&'a [u8]> + From<Error<'a>>,
79
1372
{
80
1372
    (
81
1372
        char('%'),
82
1372
        take_while_m_n(2, 2, is_hex_digit_upper).map(|v| {
83
12
            // TODO do without a dep here?
84
12
            hex::decode(v).unwrap()
85
1372
        }),
86
1372
    )
87
1372
        .map(|(_, v)| v)
88
1372
        .parse(input)
89
1372
}
90

            
91
#[inline]
92
1360
const fn is_sub_delim(b: u8) -> bool {
93
1318
    matches!(
94
1360
        b,
95
        b'!' | b'$' | b'&' | b'\'' | b'(' | b')' | b'*' | b'+' | b',' | b';' | b'='
96
    )
97
1360
}
98

            
99
266
fn authority<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], Authority, E>
100
266
where
101
266
    E: ParseError<&'a [u8]>
102
266
        + nom::error::FromExternalError<&'a [u8], nom::Err<E>>
103
266
        + From<Error<'a>>,
104
266
{
105
266
    (
106
266
        opt((user_info, char('@')).map(|(u, _)| u)),
107
266
        host,
108
266
        opt((char(':'), port).map(|(_, p)| p)),
109
266
    )
110
266
        .map(|(user_info, host, port)| Authority {
111
266
            user_info,
112
266
            host,
113
266
            port,
114
266
        })
115
266
        .parse(input)
116
266
}
117

            
118
10
fn port<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], u16, E>
119
10
where
120
10
    E: ParseError<&'a [u8]>
121
10
        + nom::error::FromExternalError<&'a [u8], nom::Err<E>>
122
10
        + From<Error<'a>>,
123
10
{
124
10
    map_res(take_while(AsChar::is_dec_digit), |c| {
125
10
        std::str::from_utf8(c)
126
10
            .map_err(|e| {
127
                nom::Err::Error(
128
                    Error::new(
129
                        input,
130
                        InnerError::EncodingError("Recur month list".to_string(), e),
131
                    )
132
                    .into(),
133
                )
134
10
            })?
135
10
            .parse::<u16>()
136
10
            .map_err(|_| nom::Err::Error(Error::new(input, InnerError::InvalidPort).into()))
137
10
    })
138
10
    .parse(input)
139
10
}
140

            
141
266
fn user_info<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], Vec<u8>, E>
142
266
where
143
266
    E: ParseError<&'a [u8]> + From<Error<'a>>,
144
266
{
145
266
    fold_many1(
146
266
        alt((
147
2982
            single(is_unreserved).map(|c| vec![c]),
148
266
            pct_encoded,
149
266
            single(is_sub_delim).map(|c| vec![c]),
150
266
            tag(":").map(|c: &[u8]| c.to_vec()),
151
266
        )),
152
266
        Vec::new,
153
2992
        |mut acc, item| {
154
2992
            acc.extend(item);
155
2992
            acc
156
2992
        },
157
266
    )
158
266
    .parse(input)
159
266
}
160

            
161
266
fn host<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], Host, E>
162
266
where
163
266
    E: ParseError<&'a [u8]>
164
266
        + nom::error::FromExternalError<&'a [u8], nom::Err<E>>
165
266
        + From<Error<'a>>,
166
266
{
167
266
    alt((
168
266
        ip_literal.map(Host::IpAddr),
169
266
        ip_v4_addr
170
266
            .map(|ip| IpAddr::V4(Ipv4Addr::from(ip)))
171
266
            .map(Host::IpAddr),
172
266
        reg_name.map(Host::RegName),
173
266
    ))
174
266
    .parse(input)
175
266
}
176

            
177
266
fn ip_literal<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], IpAddr, E>
178
266
where
179
266
    E: ParseError<&'a [u8]>
180
266
        + nom::error::FromExternalError<&'a [u8], nom::Err<E>>
181
266
        + From<Error<'a>>,
182
266
{
183
266
    (
184
266
        tag("["),
185
266
        alt((
186
266
            ip_v6_addr.map(IpAddr::V6),
187
266
            ip_v_future_addr.map(|ip| IpAddr::VFuture(ip.to_vec())),
188
266
        )),
189
266
        tag("]"),
190
266
    )
191
266
        .map(|(_, v, _)| v)
192
266
        .parse(input)
193
266
}
194

            
195
fn ip_v_future_addr<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], &'a [u8], E>
196
where
197
    E: ParseError<&'a [u8]> + From<Error<'a>>,
198
{
199
    recognize((
200
        char('v').map(|a| a as u8),
201
        take_while1(is_hex_digit),
202
        char('.'),
203
        many1(alt((
204
            single(is_unreserved),
205
            single(is_sub_delim),
206
            char(':').map(|c| c as u8),
207
        ))),
208
    ))
209
    .parse(input)
210
}
211

            
212
4
fn ip_v6_addr<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], Ipv6Addr, E>
213
4
where
214
4
    E: ParseError<&'a [u8]>
215
4
        + nom::error::FromExternalError<&'a [u8], nom::Err<E>>
216
4
        + From<Error<'a>>,
217
4
{
218
4
    let (input, prefix_parts) = separated_list0(char(':'), h_16).parse(input)?;
219

            
220
4
    if prefix_parts.len() > 7 {
221
        return Err(nom::Err::Error(
222
            Error::new(input, InnerError::InvalidIpv6).into(),
223
        ));
224
4
    }
225

            
226
4
    let (input, found_collapse) = opt(tag("::")).parse(input)?;
227
4
    let fill_zeroes = found_collapse.is_some();
228

            
229
4
    let (input, suffix_parts) = separated_list0(char(':'), h_16).parse(input)?;
230

            
231
4
    if suffix_parts.len() > 8 {
232
        return Err(nom::Err::Error(
233
            Error::new(input, InnerError::InvalidIpv6).into(),
234
        ));
235
4
    }
236

            
237
4
    let (input, ipv4_post) = opt((char(':'), ip_v4_addr)).parse(input)?;
238

            
239
4
    let mut content = [0u8; 16];
240

            
241
4
    let provided_len =
242
4
        prefix_parts.len() * 2 + suffix_parts.len() * 2 + if ipv4_post.is_some() { 4 } else { 0 };
243

            
244
4
    if provided_len > 16 || (provided_len < 16 && !fill_zeroes) {
245
        return Err(nom::Err::Error(
246
            Error::new(input, InnerError::InvalidIpv6).into(),
247
        ));
248
4
    }
249
4

            
250
4
    let mut i = 0;
251
12
    for [a, b] in prefix_parts {
252
8
        content[i] = a;
253
8
        content[i + 1] = b;
254
8
        i += 2;
255
8
    }
256

            
257
4
    if fill_zeroes {
258
4
        let zeroes = 16 - provided_len;
259
4
        i += zeroes;
260
4
    }
261

            
262
8
    for [a, b] in suffix_parts {
263
4
        content[i] = a;
264
4
        content[i + 1] = b;
265
4
        i += 2;
266
4
    }
267

            
268
4
    if let Some((_, ipv4)) = ipv4_post {
269
        content[12] = ipv4[0];
270
        content[13] = ipv4[1];
271
        content[14] = ipv4[2];
272
        content[15] = ipv4[3];
273
4
    }
274

            
275
4
    Ok((input, Ipv6Addr::from(content)))
276
4
}
277

            
278
16
fn h_16<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], [u8; 2], E>
279
16
where
280
16
    E: ParseError<&'a [u8]> + From<Error<'a>>,
281
16
{
282
16
    take_while_m_n(1, 4, is_hex_digit)
283
16
        .map(|c: &[u8]| {
284
12
            let mut src = c.to_vec();
285
28
            while src.len() < 4 {
286
16
                src.insert(0, b'0');
287
16
            }
288
12
            let mut dst = [0, 0];
289
12
            hex::decode_to_slice(src, &mut dst).unwrap();
290
12
            dst
291
16
        })
292
16
        .parse(input)
293
16
}
294

            
295
264
fn ip_v4_addr<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], [u8; 4], E>
296
264
where
297
264
    E: ParseError<&'a [u8]>
298
264
        + nom::error::FromExternalError<&'a [u8], nom::Err<E>>
299
264
        + From<Error<'a>>,
300
264
{
301
264
    (
302
264
        dec_octet,
303
264
        char('.'),
304
264
        dec_octet,
305
264
        char('.'),
306
264
        dec_octet,
307
264
        char('.'),
308
264
        dec_octet,
309
264
    )
310
264
        .map(|(a, _, b, _, c, _, d)| [a, b, c, d])
311
264
        .parse(input)
312
264
}
313

            
314
270
fn dec_octet<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], u8, E>
315
270
where
316
270
    E: ParseError<&'a [u8]>
317
270
        + nom::error::FromExternalError<&'a [u8], nom::Err<E>>
318
270
        + From<Error<'a>>,
319
270
{
320
270
    map_res(
321
270
        verify(take_while_m_n(1, 3, AsChar::is_dec_digit), |b: &[u8]| {
322
8
            // May not have a 0 prefix
323
8
            if b.len() == 2 {
324
2
                b[0] != b'0'
325
6
            } else if b.len() == 3 {
326
2
                if b[0] == b'0' && b[1] == b'0' {
327
                    false
328
                } else {
329
2
                    b[0] != b'0'
330
                }
331
            } else {
332
4
                true
333
            }
334
270
        }),
335
270
        |b| {
336
8
            std::str::from_utf8(b)
337
8
                .unwrap()
338
8
                .parse::<u8>()
339
8
                .map_err(|_| nom::Err::Error(Error::new(input, InnerError::InvalidOctet).into()))
340
270
        },
341
270
    )
342
270
    .parse(input)
343
270
}
344

            
345
262
fn reg_name<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], Vec<u8>, E>
346
262
where
347
262
    E: ParseError<&'a [u8]> + From<Error<'a>>,
348
262
{
349
262
    fold_many0(
350
262
        alt((
351
2926
            single(is_unreserved).map(|c| vec![c]),
352
262
            pct_encoded,
353
262
            single(is_sub_delim).map(|c| vec![c]),
354
262
        )),
355
262
        Vec::new,
356
2926
        |mut acc, item| {
357
2926
            acc.extend(item);
358
2926
            acc
359
2926
        },
360
262
    )
361
262
    .parse(input)
362
262
}
363

            
364
538
fn path_absolute_empty<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], &'a [u8], E>
365
538
where
366
538
    E: ParseError<&'a [u8]> + From<Error<'a>>,
367
538
{
368
538
    recognize(many0((char('/'), segment))).parse(input)
369
538
}
370

            
371
272
fn path_absolute<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], &'a [u8], E>
372
272
where
373
272
    E: ParseError<&'a [u8]> + From<Error<'a>>,
374
272
{
375
272
    recognize((segment_nz, path_absolute_empty)).parse(input)
376
272
}
377

            
378
fn path_rootless<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], &'a [u8], E>
379
where
380
    E: ParseError<&'a [u8]> + From<Error<'a>>,
381
{
382
    recognize((segment_nz, many0((char('/'), segment)))).parse(input)
383
}
384

            
385
304
fn segment<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], Vec<u8>, E>
386
304
where
387
304
    E: ParseError<&'a [u8]> + From<Error<'a>>,
388
304
{
389
1644
    fold_many0(p_char, Vec::new, |mut acc, item| {
390
1644
        acc.extend(item);
391
1644
        acc
392
1644
    })
393
304
    .parse(input)
394
304
}
395

            
396
272
fn segment_nz<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], Vec<u8>, E>
397
272
where
398
272
    E: ParseError<&'a [u8]> + From<Error<'a>>,
399
272
{
400
4598
    fold_many1(p_char, Vec::new, |mut acc, item| {
401
4598
        acc.extend(item);
402
4598
        acc
403
4598
    })
404
272
    .parse(input)
405
272
}
406

            
407
8
fn query_or_fragment<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], &'a [u8], E>
408
8
where
409
8
    E: ParseError<&'a [u8]> + From<Error<'a>>,
410
8
{
411
8
    recognize(many0(alt((
412
8
        p_char,
413
8
        tag("/").map(|c: &[u8]| c.to_vec()),
414
8
        tag("?").map(|c: &[u8]| c.to_vec()),
415
8
    ))))
416
8
    .parse(input)
417
8
}
418

            
419
6966
fn p_char<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], Vec<u8>, E>
420
6966
where
421
6966
    E: ParseError<&'a [u8]> + From<Error<'a>>,
422
6966
{
423
6966
    alt((
424
6966
        single(is_unreserved).map(|c| vec![c]),
425
6966
        pct_encoded,
426
6966
        single(is_sub_delim).map(|c| vec![c]),
427
6966
        tag(":").map(|c: &[u8]| c.to_vec()),
428
6966
        tag("@").map(|c: &[u8]| c.to_vec()),
429
6966
    ))
430
6966
    .parse(input)
431
6966
}
432

            
433
impl Display for Uri<'_> {
434
60
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
435
60
        write!(f, "{}", String::from_utf8_lossy(self.scheme))?;
436
60
        f.write_char(':')?;
437

            
438
60
        if let Some(authority) = &self.authority {
439
52
            f.write_char('/')?;
440
52
            f.write_char('/')?;
441

            
442
52
            if let Some(user_info) = &authority.user_info {
443
                write!(f, "{}", String::from_utf8_lossy(user_info))?;
444
                f.write_char('@')?;
445
52
            }
446

            
447
4
            match &authority.host {
448
2
                Host::IpAddr(IpAddr::V4(ip)) => write!(f, "{}", ip)?,
449
2
                Host::IpAddr(IpAddr::V6(ip)) => {
450
2
                    f.write_char('[')?;
451
2
                    write!(f, "{}", ip)?;
452
2
                    f.write_char(']')?;
453
                }
454
                Host::IpAddr(IpAddr::VFuture(vf)) => {
455
                    f.write_char('[')?;
456
                    vf.iter().try_for_each(|b| write!(f, "{:02X}", b))?;
457
                    f.write_char(']')?;
458
                }
459
48
                Host::RegName(name) => write!(f, "{}", String::from_utf8_lossy(name))?,
460
            }
461

            
462
52
            if let Some(port) = authority.port {
463
2
                write!(f, ":{}", port)?;
464
50
            }
465
8
        };
466

            
467
60
        write!(f, "{}", String::from_utf8_lossy(&self.path))?;
468

            
469
60
        if let Some(query) = self.query {
470
2
            f.write_char('?')?;
471
2
            write!(f, "{}", String::from_utf8_lossy(query))?;
472
58
        }
473

            
474
60
        if let Some(fragment) = self.fragment {
475
            f.write_char('#')?;
476
            write!(f, "{}", String::from_utf8_lossy(fragment))?;
477
60
        }
478

            
479
60
        Ok(())
480
60
    }
481
}
482

            
483
#[cfg(test)]
484
mod tests {
485
    use super::*;
486
    use crate::parser::types::{Host, IpAddr, Uri};
487
    use crate::test_utils::check_rem;
488

            
489
    #[test]
490
2
    fn ftp() {
491
2
        let raw = b"ftp://ftp.is.co.za/rfc/rfc1808.txt`";
492
2
        let (input, uri) = param_value_uri::<Error>(raw).unwrap();
493
2
        check_rem(input, 1);
494
2
        assert_eq!(uri.scheme, b"ftp");
495
2
        assert_eq!(
496
2
            uri.authority.clone().unwrap().host,
497
2
            Host::RegName(b"ftp.is.co.za".to_vec())
498
2
        );
499
2
        assert_eq!(uri.path, b"/rfc/rfc1808.txt");
500
2
        check_serialize_raw(uri, raw);
501
2
    }
502

            
503
    #[test]
504
2
    fn http() {
505
2
        let raw = b"http://www.ietf.org/rfc/rfc2396.txt`";
506
2
        let (input, uri) = param_value_uri::<Error>(raw).unwrap();
507
2
        check_rem(input, 1);
508
2
        assert_eq!(uri.scheme, b"http");
509
2
        assert_eq!(
510
2
            uri.authority.clone().unwrap().host,
511
2
            Host::RegName(b"www.ietf.org".to_vec())
512
2
        );
513
2
        assert_eq!(uri.path, b"/rfc/rfc2396.txt");
514
2
        check_serialize_raw(uri, raw);
515
2
    }
516

            
517
    #[test]
518
2
    fn ip_v6() {
519
2
        let (input, ipv6) = ip_v6_addr::<Error>(b"2001:db8::7`").unwrap();
520
2
        check_rem(input, 1);
521
2
        assert_eq!(ipv6, Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 7));
522
2
    }
523

            
524
    #[test]
525
2
    fn ldap() {
526
2
        let raw = b"ldap://[2001:db8::7]/c=GB?objectClass?one`";
527
2
        let (input, uri) = param_value_uri::<Error>(raw).unwrap();
528
2
        check_rem(input, 1);
529
2
        assert_eq!(uri.scheme, b"ldap");
530
2
        assert_eq!(
531
2
            uri.authority.clone().unwrap().host,
532
2
            Host::IpAddr(IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 7)))
533
2
        );
534
2
        assert_eq!(uri.path, b"/c=GB");
535
2
        assert_eq!(uri.query.unwrap(), b"objectClass?one");
536
2
        check_serialize_raw(uri, raw);
537
2
    }
538

            
539
    #[test]
540
2
    fn mailto() {
541
2
        let raw = b"mailto:John.Doe@example.com`";
542
2
        let (input, uri) = param_value_uri::<Error>(raw).unwrap();
543
2
        check_rem(input, 1);
544
2
        assert_eq!(uri.scheme, b"mailto");
545
2
        assert_eq!(uri.path, b"John.Doe@example.com".to_vec());
546
2
        check_serialize_raw(uri, raw);
547
2
    }
548

            
549
    #[test]
550
2
    fn news() {
551
2
        let raw = b"news:comp.infosystems.www.servers.unix`";
552
2
        let (input, uri) = param_value_uri::<Error>(raw).unwrap();
553
2
        check_rem(input, 1);
554
2
        assert_eq!(uri.scheme, b"news");
555
2
        assert_eq!(uri.path, b"comp.infosystems.www.servers.unix".to_vec());
556
2
        check_serialize_raw(uri, raw);
557
2
    }
558

            
559
    #[test]
560
2
    fn tel() {
561
2
        let raw = b"tel:+1-816-555-1212`";
562
2
        let (input, uri) = param_value_uri::<Error>(raw).unwrap();
563
2
        check_rem(input, 1);
564
2
        assert_eq!(uri.scheme, b"tel");
565
2
        assert_eq!(uri.path, b"+1-816-555-1212".to_vec());
566
2
        check_serialize_raw(uri, raw);
567
2
    }
568

            
569
    #[test]
570
2
    fn telnet() {
571
2
        let raw = b"telnet://192.0.2.16:80/`";
572
2
        let (input, uri) = param_value_uri::<Error>(raw).unwrap();
573
2
        check_rem(input, 1);
574
2
        assert_eq!(uri.scheme, b"telnet");
575
2
        let authority = uri.authority.clone().unwrap();
576
2
        assert_eq!(
577
2
            authority.host,
578
2
            Host::IpAddr(IpAddr::V4(Ipv4Addr::new(192, 0, 2, 16)))
579
2
        );
580
2
        assert_eq!(authority.port.unwrap(), 80);
581
2
        check_serialize_raw(uri, raw);
582
2
    }
583

            
584
    #[test]
585
2
    fn urn() {
586
2
        let raw = b"urn:oasis:names:specification:docbook:dtd:xml:4.1.2`";
587
2
        let (input, uri) = param_value_uri::<Error>(raw).unwrap();
588
2
        check_rem(input, 1);
589
2
        assert_eq!(uri.scheme, b"urn");
590
2
        assert_eq!(
591
2
            uri.path,
592
2
            b"oasis:names:specification:docbook:dtd:xml:4.1.2".to_vec()
593
2
        );
594
2
        check_serialize_raw(uri, raw);
595
2
    }
596

            
597
16
    fn check_serialize_raw(uri: Uri, raw: &[u8]) {
598
16
        let out = uri.to_string();
599
16
        assert_eq!(out.as_bytes(), &raw[..(raw.len() - 1)]);
600
16
    }
601
}