Coverage Report

Created: 2025-08-03 06:24

/src/unit/src/nxt_string.c
Line
Count
Source (jump to first uncovered line)
1
2
/*
3
 * Copyright (C) Igor Sysoev
4
 * Copyright (C) NGINX, Inc.
5
 */
6
7
#include <nxt_main.h>
8
9
10
nxt_str_t *
11
nxt_str_alloc(nxt_mp_t *mp, size_t length)
12
0
{
13
0
    nxt_str_t  *s;
14
15
    /* The string start is allocated aligned to be close to nxt_str_t. */
16
0
    s = nxt_mp_get(mp, sizeof(nxt_str_t) + length);
17
18
0
    if (nxt_fast_path(s != NULL)) {
19
0
        s->length = length;
20
0
        s->start = nxt_pointer_to(s, sizeof(nxt_str_t));
21
0
    }
22
23
0
    return s;
24
0
}
25
26
27
/*
28
 * nxt_str_dup() creates a new string with a copy of a source string.
29
 * If length of the source string is zero, then the new string anyway
30
 * gets a pointer somewhere in mem_pool.
31
 */
32
33
nxt_str_t *
34
nxt_str_dup(nxt_mp_t *mp, nxt_str_t *dst, const nxt_str_t *src)
35
0
{
36
0
    u_char  *p;
37
38
0
    if (dst == NULL) {
39
        /* The string start is allocated aligned to be close to nxt_str_t. */
40
0
        dst = nxt_mp_get(mp, sizeof(nxt_str_t) + src->length);
41
0
        if (nxt_slow_path(dst == NULL)) {
42
0
            return NULL;
43
0
        }
44
45
0
        p = (u_char *) dst;
46
0
        p += sizeof(nxt_str_t);
47
0
        dst->start = p;
48
49
0
    } else {
50
0
        dst->start = nxt_mp_nget(mp, src->length);
51
0
        if (nxt_slow_path(dst->start == NULL)) {
52
0
            return NULL;
53
0
        }
54
0
    }
55
56
0
    nxt_memcpy(dst->start, src->start, src->length);
57
0
    dst->length = src->length;
58
59
0
    return dst;
60
0
}
61
62
63
/*
64
 * nxt_str_cstrz() creates a C style zero-terminated copy of a source
65
 * nxt_str_t.  The function is intended to create strings suitable
66
 * for libc and kernel interfaces so result is pointer to char instead
67
 * of u_char to minimize casts.
68
 */
69
70
char *
71
nxt_str_cstrz(nxt_mp_t *mp, const nxt_str_t *src)
72
0
{
73
0
    char  *p, *dst;
74
75
0
    dst = nxt_mp_alloc(mp, src->length + 1);
76
77
0
    if (nxt_fast_path(dst != NULL)) {
78
0
        p = nxt_cpymem(dst, src->start, src->length);
79
0
        *p = '\0';
80
0
    }
81
82
0
    return dst;
83
0
}
84
85
86
void
87
nxt_memcpy_lowcase(u_char *dst, const u_char *src, size_t length)
88
151
{
89
151
    u_char  c;
90
91
9.39k
    while (length != 0) {
92
9.24k
        c = *src++;
93
9.24k
        *dst++ = nxt_lowcase(c);
94
9.24k
        length--;
95
9.24k
    }
96
151
}
97
98
99
void
100
nxt_memcpy_upcase(u_char *dst, const u_char *src, size_t length)
101
0
{
102
0
    u_char  c;
103
104
0
    while (length != 0) {
105
0
        c = *src++;
106
0
        *dst++ = nxt_upcase(c);
107
0
        length--;
108
0
    }
109
0
}
110
111
112
u_char *
113
nxt_cpystr(u_char *dst, const u_char *src)
114
0
{
115
0
    for ( ;; ) {
116
0
        *dst = *src;
117
118
0
        if (*dst == '\0') {
119
0
            break;
120
0
        }
121
122
0
        dst++;
123
0
        src++;
124
0
    }
125
126
0
    return dst;
127
0
}
128
129
130
u_char *
131
nxt_cpystrn(u_char *dst, const u_char *src, size_t length)
132
0
{
133
0
    if (length == 0) {
134
0
        return dst;
135
0
    }
136
137
0
    while (--length != 0) {
138
0
        *dst = *src;
139
140
0
        if (*dst == '\0') {
141
0
            return dst;
142
0
        }
143
144
0
        dst++;
145
0
        src++;
146
0
    }
147
148
0
    *dst = '\0';
149
150
0
    return dst;
151
0
}
152
153
154
nxt_int_t
155
nxt_strcasecmp(const u_char *s1, const u_char *s2)
156
0
{
157
0
    u_char     c1, c2;
158
0
    nxt_int_t  n;
159
160
0
    for ( ;; ) {
161
0
        c1 = *s1++;
162
0
        c2 = *s2++;
163
164
0
        c1 = nxt_lowcase(c1);
165
0
        c2 = nxt_lowcase(c2);
166
167
0
        n = c1 - c2;
168
169
0
        if (n != 0) {
170
0
            return n;
171
0
        }
172
173
0
        if (c1 == 0) {
174
0
            return 0;
175
0
        }
176
0
    }
177
0
}
178
179
180
nxt_int_t
181
nxt_strncasecmp(const u_char *s1, const u_char *s2, size_t length)
182
0
{
183
0
    u_char     c1, c2;
184
0
    nxt_int_t  n;
185
186
0
    while (length-- != 0) {
187
0
        c1 = *s1++;
188
0
        c2 = *s2++;
189
190
0
        c1 = nxt_lowcase(c1);
191
0
        c2 = nxt_lowcase(c2);
192
193
0
        n = c1 - c2;
194
195
0
        if (n != 0) {
196
0
            return n;
197
0
        }
198
199
0
        if (c1 == 0) {
200
0
            return 0;
201
0
        }
202
0
    }
203
204
0
    return 0;
205
0
}
206
207
208
nxt_int_t
209
nxt_memcasecmp(const void *p1, const void *p2, size_t length)
210
3.74k
{
211
3.74k
    u_char        c1, c2;
212
3.74k
    nxt_int_t     n;
213
3.74k
    const u_char  *s1, *s2;
214
215
3.74k
    s1 = p1;
216
3.74k
    s2 = p2;
217
218
33.0k
    while (length-- != 0) {
219
30.6k
        c1 = *s1++;
220
30.6k
        c2 = *s2++;
221
222
30.6k
        c1 = nxt_lowcase(c1);
223
30.6k
        c2 = nxt_lowcase(c2);
224
225
30.6k
        n = c1 - c2;
226
227
30.6k
        if (n != 0) {
228
1.30k
            return n;
229
1.30k
        }
230
30.6k
    }
231
232
2.43k
    return 0;
233
3.74k
}
234
235
236
/*
237
 * nxt_memstrn() is intended for search of static substring "ss"
238
 * with known length "length" in string "s" limited by parameter "end".
239
 * Zeros are ignored in both strings.
240
 */
241
242
u_char *
243
nxt_memstrn(const u_char *s, const u_char *end, const char *ss, size_t length)
244
0
{
245
0
    u_char  c1, c2, *s2;
246
247
0
    s2 = (u_char *) ss;
248
0
    c2 = *s2++;
249
0
    length--;
250
251
0
    while (s < end) {
252
0
        c1 = *s++;
253
254
0
        if (c1 == c2) {
255
256
0
            if (s + length > end) {
257
0
                return NULL;
258
0
            }
259
260
0
            if (memcmp(s, s2, length) == 0) {
261
0
                return (u_char *) s - 1;
262
0
            }
263
0
        }
264
0
    }
265
266
0
    return NULL;
267
0
}
268
269
270
/*
271
 * nxt_strcasestrn() is intended for caseless search of static substring
272
 * "ss" with known length "length" in string "s" limited by parameter "end".
273
 * Zeros are ignored in both strings.
274
 */
275
276
u_char *
277
nxt_memcasestrn(const u_char *s, const u_char *end, const char *ss,
278
    size_t length)
279
1.80k
{
280
1.80k
    u_char  c1, c2, *s2;
281
282
1.80k
    s2 = (u_char *) ss;
283
1.80k
    c2 = *s2++;
284
1.80k
    c2 = nxt_lowcase(c2);
285
1.80k
    length--;
286
287
13.1k
    while (s < end) {
288
11.8k
        c1 = *s++;
289
11.8k
        c1 = nxt_lowcase(c1);
290
291
11.8k
        if (c1 == c2) {
292
293
1.34k
            if (s + length > end) {
294
237
                return NULL;
295
237
            }
296
297
1.10k
            if (nxt_memcasecmp(s, s2, length) == 0) {
298
271
                return (u_char *) s - 1;
299
271
            }
300
1.10k
        }
301
11.8k
    }
302
303
1.29k
    return NULL;
304
1.80k
}
305
306
307
/*
308
 * nxt_rstrstrn() is intended to search for static substring "ss"
309
 * with known length "length" in string "s" limited by parameter "end"
310
 * in reverse order.  Zeros are ignored in both strings.
311
 */
312
313
u_char *
314
nxt_rmemstrn(const u_char *s, const u_char *end, const char *ss, size_t length)
315
0
{
316
0
    u_char        c1, c2;
317
0
    const u_char  *s1, *s2;
318
319
0
    s1 = end - length;
320
0
    s2 = (u_char *) ss;
321
0
    c2 = *s2++;
322
0
    length--;
323
324
0
    while (s < s1) {
325
0
        c1 = *s1;
326
327
0
        if (c1 == c2) {
328
0
            if (memcmp(s1 + 1, s2, length) == 0) {
329
0
                return (u_char *) s1;
330
0
            }
331
0
        }
332
333
0
        s1--;
334
0
    }
335
336
0
    return NULL;
337
0
}
338
339
340
size_t
341
nxt_str_strip(const u_char *start, u_char *end)
342
0
{
343
0
    u_char  *p;
344
345
0
    for (p = end - 1; p >= start; p--) {
346
0
        if (*p != '\r' && *p != '\n') {
347
0
            break;
348
0
        }
349
0
    }
350
351
0
    return (p + 1) - start;
352
0
}
353
354
355
nxt_int_t
356
nxt_strverscmp(const u_char *s1, const u_char *s2)
357
0
{
358
0
    u_char     c1, c2;
359
0
    nxt_int_t  diff;
360
361
0
    enum {
362
0
        st_str = 0,
363
0
        st_num,
364
0
        st_zero,
365
0
        st_frac,
366
0
    } state;
367
368
0
    state = st_str;
369
370
0
    for ( ;; ) {
371
0
        c1 = *s1++;
372
0
        c2 = *s2++;
373
374
0
        diff = c1 - c2;
375
376
0
        if (diff != 0) {
377
0
            break;
378
0
        }
379
380
0
        if (c1 == '\0') {
381
0
            return 0;
382
0
        }
383
384
0
        if (!nxt_isdigit(c1)) {
385
0
            state = st_str;
386
0
            continue;
387
0
        }
388
389
0
        if (state == st_str) {
390
0
            state = (c1 != '0') ? st_num : st_zero;
391
0
            continue;
392
0
        }
393
394
0
        if (state == st_zero && c1 != '0') {
395
0
            state = st_frac;
396
0
            continue;
397
0
        }
398
0
    }
399
400
0
    switch (state) {
401
402
0
    case st_str:
403
404
0
        if ((u_char) (c1 - '1') > 8 || (u_char) (c2 - '1') > 8) {
405
0
            return diff;
406
0
        }
407
408
0
        c1 = *s1++;
409
0
        c2 = *s2++;
410
411
        /* Fall through. */
412
413
0
    case st_num:
414
415
0
        while (nxt_isdigit(c1) && nxt_isdigit(c2)) {
416
0
            c1 = *s1++;
417
0
            c2 = *s2++;
418
0
        }
419
420
0
        if (nxt_isdigit(c1)) {
421
0
            return 1;
422
0
        }
423
424
0
        if (nxt_isdigit(c2)) {
425
0
            return -1;
426
0
        }
427
428
0
        return diff;
429
430
0
    case st_zero:
431
432
0
        if (c1 == '0' || c2 == '\0') {
433
0
            return -1;
434
0
        }
435
436
0
        if (c2 == '0' || c1 == '\0') {
437
0
            return 1;
438
0
        }
439
440
        /* Fall through. */
441
442
0
    case st_frac:
443
0
    default:
444
0
        return diff;
445
0
    }
446
0
}
447
448
449
nxt_bool_t
450
nxt_strvers_match(u_char *version, u_char *prefix, size_t length)
451
0
{
452
0
    u_char  next, last;
453
454
0
    if (length == 0) {
455
0
        return 1;
456
0
    }
457
458
0
    if (nxt_strncmp(version, prefix, length) == 0) {
459
460
0
        next = version[length];
461
462
0
        if (next == '\0') {
463
0
            return 1;
464
0
        }
465
466
0
        last = version[length - 1];
467
468
0
        if (nxt_isdigit(last) != nxt_isdigit(next)) {
469
            /* This is a version part boundary. */
470
0
            return 1;
471
0
        }
472
0
    }
473
474
0
    return 0;
475
0
}
476
477
478
const uint8_t  nxt_hex2int[256]
479
    nxt_aligned(32) =
480
{
481
    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
482
    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
483
    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
484
     0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 16, 16, 16, 16, 16, 16,
485
    16, 10, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16,
486
    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
487
    16, 10, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16,
488
    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
489
    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
490
    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
491
    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
492
    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
493
    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
494
    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
495
    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
496
    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
497
};
498
499
500
static const uint32_t  nxt_uri_escape[] = {
501
    0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
502
503
                /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */
504
    0xd000002d, /* 1101 0000 0000 0000 0000 0000 0010 1101 */
505
506
                /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */
507
    0x50000000, /* 0101 0000 0000 0000 0000 0000 0000 0000 */
508
509
                /*  ~}| {zyx wvut srqp onml kjih gfed cba` */
510
    0xb8000001, /* 1011 1000 0000 0000 0000 0000 0000 0001 */
511
512
    0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
513
    0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
514
    0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
515
    0xffffffff  /* 1111 1111 1111 1111 1111 1111 1111 1111 */
516
};
517
518
519
u_char *
520
nxt_decode_uri(u_char *dst, u_char *src, size_t length)
521
0
{
522
0
    u_char   *end, ch;
523
0
    uint8_t  d0, d1;
524
525
0
    nxt_prefetch(&nxt_hex2int['0']);
526
527
0
    end = src + length;
528
529
0
    while (src < end) {
530
0
        ch = *src++;
531
532
0
        if (ch == '%') {
533
0
            if (nxt_slow_path(end - src < 2)) {
534
0
                return NULL;
535
0
            }
536
537
0
            d0 = nxt_hex2int[*src++];
538
0
            d1 = nxt_hex2int[*src++];
539
540
0
            if (nxt_slow_path((d0 | d1) >= 16)) {
541
0
                return NULL;
542
0
            }
543
544
0
            ch = (d0 << 4) + d1;
545
0
        }
546
547
0
        *dst++ = ch;
548
0
    }
549
550
0
    return dst;
551
0
}
552
553
554
u_char *
555
nxt_decode_uri_plus(u_char *dst, u_char *src, size_t length)
556
0
{
557
0
    u_char   *end, ch;
558
0
    uint8_t  d0, d1;
559
560
0
    nxt_prefetch(&nxt_hex2int['0']);
561
562
0
    end = src + length;
563
564
0
    while (src < end) {
565
0
        ch = *src++;
566
567
0
        switch (ch) {
568
0
        case '%':
569
0
            if (nxt_slow_path(end - src < 2)) {
570
0
                return NULL;
571
0
            }
572
573
0
            d0 = nxt_hex2int[*src++];
574
0
            d1 = nxt_hex2int[*src++];
575
576
0
            if (nxt_slow_path((d0 | d1) >= 16)) {
577
0
                return NULL;
578
0
            }
579
580
0
            ch = (d0 << 4) + d1;
581
0
            break;
582
583
0
        case '+':
584
0
            ch = ' ';
585
0
            break;
586
0
        }
587
588
0
        *dst++ = ch;
589
0
    }
590
591
0
    return dst;
592
0
}
593
594
595
uintptr_t
596
nxt_encode_uri(u_char *dst, u_char *src, size_t length)
597
0
{
598
0
    u_char      *end;
599
0
    nxt_uint_t  n;
600
601
0
    static const u_char  hex[16] NXT_NONSTRING = "0123456789ABCDEF";
602
603
0
    end = src + length;
604
605
0
    if (dst == NULL) {
606
607
        /* Find the number of the characters to be escaped. */
608
609
0
        n = 0;
610
611
0
        while (src < end) {
612
613
0
            if (nxt_uri_escape[*src >> 5] & (1U << (*src & 0x1f))) {
614
0
                n++;
615
0
            }
616
617
0
            src++;
618
0
        }
619
620
0
        return (uintptr_t) n;
621
0
    }
622
623
0
    while (src < end) {
624
625
0
        if (nxt_uri_escape[*src >> 5] & (1U << (*src & 0x1f))) {
626
0
            *dst++ = '%';
627
0
            *dst++ = hex[*src >> 4];
628
0
            *dst++ = hex[*src & 0xf];
629
630
0
        } else {
631
0
            *dst++ = *src;
632
0
        }
633
634
0
        src++;
635
0
    }
636
637
0
    return (uintptr_t) dst;
638
0
}
639
640
641
uintptr_t
642
nxt_encode_complex_uri(u_char *dst, u_char *src, size_t length)
643
0
{
644
0
    u_char      *reserved, *end, ch;
645
0
    nxt_uint_t  n;
646
647
0
    static const u_char  hex[16] NXT_NONSTRING = "0123456789ABCDEF";
648
649
0
    reserved = (u_char *) "?#\0";
650
651
0
    end = src + length;
652
653
0
    if (dst == NULL) {
654
655
        /* Find the number of the characters to be escaped. */
656
657
0
        n = 0;
658
659
0
        while (src < end) {
660
0
            ch = *src++;
661
662
0
            if (nxt_uri_escape[ch >> 5] & (1U << (ch & 0x1f))) {
663
0
                if (ch == reserved[0]) {
664
0
                    reserved++;
665
0
                    continue;
666
0
                }
667
668
0
                if (ch == reserved[1]) {
669
0
                    reserved += 2;
670
0
                    continue;
671
0
                }
672
673
0
                n++;
674
0
            }
675
0
        }
676
677
0
        return (uintptr_t) n;
678
0
    }
679
680
0
    while (src < end) {
681
0
        ch = *src++;
682
683
0
        if (nxt_uri_escape[ch >> 5] & (1U << (ch & 0x1f))) {
684
0
            if (ch == reserved[0]) {
685
0
                reserved++;
686
687
0
            } else if (ch == reserved[1]) {
688
0
                reserved += 2;
689
690
0
            } else {
691
0
                *dst++ = '%';
692
0
                *dst++ = hex[ch >> 4];
693
0
                *dst++ = hex[ch & 0xf];
694
0
                continue;
695
0
            }
696
0
        }
697
698
0
        *dst++ = ch;
699
0
    }
700
701
0
    return (uintptr_t) dst;
702
0
}
703
704
705
nxt_bool_t
706
nxt_is_complex_uri_encoded(u_char *src, size_t length)
707
0
{
708
0
    u_char   *reserved, *end, ch;
709
0
    uint8_t  d0, d1;
710
711
0
    reserved = (u_char *) "?#\0";
712
713
0
    for (end = src + length; src < end; src++) {
714
0
        ch = *src;
715
716
0
        if (nxt_uri_escape[ch >> 5] & (1U << (ch & 0x1f))) {
717
0
            if (ch == '%') {
718
0
                if (end - src < 2) {
719
0
                    return 0;
720
0
                }
721
722
0
                d0 = nxt_hex2int[*++src];
723
0
                d1 = nxt_hex2int[*++src];
724
725
0
                if ((d0 | d1) >= 16) {
726
0
                    return 0;
727
0
                }
728
729
0
                continue;
730
0
            }
731
732
0
            if (ch == reserved[0]) {
733
0
                reserved++;
734
0
                continue;
735
0
            }
736
737
0
            if (ch == reserved[1]) {
738
0
                reserved += 2;
739
0
                continue;
740
0
            }
741
742
0
            return 0;
743
0
        }
744
0
    }
745
746
0
    return 1;
747
0
}
748
749
750
ssize_t
751
nxt_base64_decode(u_char *dst, u_char *src, size_t length)
752
0
{
753
0
    u_char   *end, *p;
754
0
    size_t   pad;
755
0
    uint8_t  v1, v2, v3, v4;
756
757
0
    static const uint8_t  decode[] = {
758
0
        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
759
0
        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
760
0
        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 62, 77, 77, 77, 63,
761
0
        52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 77, 77, 77, 77, 77, 77,
762
0
        77,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
763
0
        15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 77, 77, 77, 77, 77,
764
0
        77, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
765
0
        41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 77, 77, 77, 77, 77,
766
767
0
        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
768
0
        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
769
0
        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
770
0
        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
771
0
        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
772
0
        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
773
0
        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
774
0
        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77
775
0
    };
776
777
0
    end = src + length;
778
0
    pad = (4 - (length % 4)) % 4;
779
780
0
    if (dst == NULL) {
781
0
        if (pad > 2) {
782
0
            return NXT_ERROR;
783
0
        }
784
785
0
        while (src < end) {
786
0
            if (decode[*src] != 77) {
787
0
                src++;
788
0
                continue;
789
0
            }
790
791
0
            if (pad == 0) {
792
0
                pad = end - src;
793
794
0
                if ((pad == 1 || (pad == 2 && src[1] == '=')) && src[0] == '=')
795
0
                {
796
0
                    break;
797
0
                }
798
0
            }
799
800
0
            return NXT_ERROR;
801
0
        }
802
803
0
        return (length + 3) / 4 * 3 - pad;
804
0
    }
805
806
0
    nxt_assert(length != 0);
807
808
0
    if (pad == 0) {
809
0
        pad = (end[-1] == '=') + (end[-2] == '=');
810
0
        end -= (pad + 3) & 4;
811
812
0
    } else {
813
0
        end -= 4 - pad;
814
0
    }
815
816
0
    p = dst;
817
818
0
    while (src < end) {
819
0
        v1 = decode[src[0]];
820
0
        v2 = decode[src[1]];
821
0
        v3 = decode[src[2]];
822
0
        v4 = decode[src[3]];
823
824
0
        *p++ = (v1 << 2 | v2 >> 4);
825
0
        *p++ = (v2 << 4 | v3 >> 2);
826
0
        *p++ = (v3 << 6 | v4);
827
828
0
        src += 4;
829
0
    }
830
831
0
    if (pad > 0) {
832
0
        v1 = decode[src[0]];
833
0
        v2 = decode[src[1]];
834
835
0
        *p++ = (v1 << 2 | v2 >> 4);
836
837
0
        if (pad == 1) {
838
0
            v3 = decode[src[2]];
839
0
            *p++ = (v2 << 4 | v3 >> 2);
840
0
        }
841
0
    }
842
843
0
    return (p - dst);
844
0
}