
/*
 * Copyright (C) Igor Sysoev
 * Copyright (C) NGINX, Inc.
 */

#include <nxt_main.h>


nxt_str_t *
nxt_str_alloc(nxt_mp_t *mp, size_t length)
{
    nxt_str_t  *s;

    /* The string start is allocated aligned to be close to nxt_str_t. */
    s = nxt_mp_get(mp, sizeof(nxt_str_t) + length);

    if (nxt_fast_path(s != NULL)) {
        s->length = length;
        s->start = nxt_pointer_to(s, sizeof(nxt_str_t));
    }

    return s;
}


/*
 * nxt_str_dup() creates a new string with a copy of a source string.
 * If length of the source string is zero, then the new string anyway
 * gets a pointer somewhere in mem_pool.
 */

nxt_str_t *
nxt_str_dup(nxt_mp_t *mp, nxt_str_t *dst, const nxt_str_t *src)
{
    u_char  *p;

    if (dst == NULL) {
        /* The string start is allocated aligned to be close to nxt_str_t. */
        dst = nxt_mp_get(mp, sizeof(nxt_str_t) + src->length);
        if (nxt_slow_path(dst == NULL)) {
            return NULL;
        }

        p = (u_char *) dst;
        p += sizeof(nxt_str_t);
        dst->start = p;

    } else {
        dst->start = nxt_mp_nget(mp, src->length);
        if (nxt_slow_path(dst->start == NULL)) {
            return NULL;
        }
    }

    nxt_memcpy(dst->start, src->start, src->length);
    dst->length = src->length;

    return dst;
}


/*
 * nxt_str_cstrz() creates a C style zero-terminated copy of a source
 * nxt_str_t.  The function is intended to create strings suitable
 * for libc and kernel interfaces so result is pointer to char instead
 * of u_char to minimize casts.
 */

char *
nxt_str_cstrz(nxt_mp_t *mp, const nxt_str_t *src)
{
    char  *p, *dst;

    dst = nxt_mp_alloc(mp, src->length + 1);

    if (nxt_fast_path(dst != NULL)) {
        p = nxt_cpymem(dst, src->start, src->length);
        *p = '\0';
    }

    return dst;
}


void
nxt_memcpy_lowcase(u_char *dst, const u_char *src, size_t length)
{
    u_char  c;

    while (length != 0) {
        c = *src++;
        *dst++ = nxt_lowcase(c);
        length--;
    }
}


void
nxt_memcpy_upcase(u_char *dst, const u_char *src, size_t length)
{
    u_char  c;

    while (length != 0) {
        c = *src++;
        *dst++ = nxt_upcase(c);
        length--;
    }
}


u_char *
nxt_cpystr(u_char *dst, const u_char *src)
{
    for ( ;; ) {
        *dst = *src;

        if (*dst == '\0') {
            break;
        }

        dst++;
        src++;
    }

    return dst;
}


u_char *
nxt_cpystrn(u_char *dst, const u_char *src, size_t length)
{
    if (length == 0) {
        return dst;
    }

    while (--length != 0) {
        *dst = *src;

        if (*dst == '\0') {
            return dst;
        }

        dst++;
        src++;
    }

    *dst = '\0';

    return dst;
}


nxt_int_t
nxt_strcasecmp(const u_char *s1, const u_char *s2)
{
    u_char     c1, c2;
    nxt_int_t  n;

    for ( ;; ) {
        c1 = *s1++;
        c2 = *s2++;

        c1 = nxt_lowcase(c1);
        c2 = nxt_lowcase(c2);

        n = c1 - c2;

        if (n != 0) {
            return n;
        }

        if (c1 == 0) {
            return 0;
        }
    }
}


nxt_int_t
nxt_strncasecmp(const u_char *s1, const u_char *s2, size_t length)
{
    u_char     c1, c2;
    nxt_int_t  n;

    while (length-- != 0) {
        c1 = *s1++;
        c2 = *s2++;

        c1 = nxt_lowcase(c1);
        c2 = nxt_lowcase(c2);

        n = c1 - c2;

        if (n != 0) {
            return n;
        }

        if (c1 == 0) {
            return 0;
        }
    }

    return 0;
}


nxt_int_t
nxt_memcasecmp(const void *p1, const void *p2, size_t length)
{
    u_char        c1, c2;
    nxt_int_t     n;
    const u_char  *s1, *s2;

    s1 = p1;
    s2 = p2;

    while (length-- != 0) {
        c1 = *s1++;
        c2 = *s2++;

        c1 = nxt_lowcase(c1);
        c2 = nxt_lowcase(c2);

        n = c1 - c2;

        if (n != 0) {
            return n;
        }
    }

    return 0;
}


/*
 * nxt_memstrn() is intended for search of static substring "ss"
 * with known length "length" in string "s" limited by parameter "end".
 * Zeros are ignored in both strings.
 */

u_char *
nxt_memstrn(const u_char *s, const u_char *end, const char *ss, size_t length)
{
    u_char  c1, c2, *s2;

    s2 = (u_char *) ss;
    c2 = *s2++;
    length--;

    while (s < end) {
        c1 = *s++;

        if (c1 == c2) {

            if (s + length > end) {
                return NULL;
            }

            if (nxt_memcmp(s, s2, length) == 0) {
                return (u_char *) s - 1;
            }
        }
    }

    return NULL;
}


/*
 * nxt_strcasestrn() is intended for caseless search of static substring
 * "ss" with known length "length" in string "s" limited by parameter "end".
 * Zeros are ignored in both strings.
 */

u_char *
nxt_memcasestrn(const u_char *s, const u_char *end, const char *ss,
    size_t length)
{
    u_char  c1, c2, *s2;

    s2 = (u_char *) ss;
    c2 = *s2++;
    c2 = nxt_lowcase(c2);
    length--;

    while (s < end) {
        c1 = *s++;
        c1 = nxt_lowcase(c1);

        if (c1 == c2) {

            if (s + length > end) {
                return NULL;
            }

            if (nxt_memcasecmp(s, s2, length) == 0) {
                return (u_char *) s - 1;
            }
        }
    }

    return NULL;
}


/*
 * nxt_rstrstrn() is intended to search for static substring "ss"
 * with known length "length" in string "s" limited by parameter "end"
 * in reverse order.  Zeros are ignored in both strings.
 */

u_char *
nxt_rmemstrn(const u_char *s, const u_char *end, const char *ss, size_t length)
{
    u_char        c1, c2;
    const u_char  *s1, *s2;

    s1 = end - length;
    s2 = (u_char *) ss;
    c2 = *s2++;
    length--;

    while (s < s1) {
        c1 = *s1;

        if (c1 == c2) {
            if (nxt_memcmp(s1 + 1, s2, length) == 0) {
                return (u_char *) s1;
            }
        }

        s1--;
    }

    return NULL;
}


size_t
nxt_str_strip(u_char *start, u_char *end)
{
    u_char  *p;

    for (p = end - 1; p >= start; p--) {
        if (*p != '\r' && *p != '\n') {
            break;
        }
    }

    return (p + 1) - start;
}


nxt_int_t
nxt_strverscmp(const u_char *s1, const u_char *s2)
{
    u_char     c1, c2;
    nxt_int_t  diff;

    enum {
        st_str = 0,
        st_num,
        st_zero,
        st_frac,
    } state;

    state = st_str;

    for ( ;; ) {
        c1 = *s1++;
        c2 = *s2++;

        diff = c1 - c2;

        if (diff != 0) {
            break;
        }

        if (c1 == '\0') {
            return 0;
        }

        if (!nxt_isdigit(c1)) {
            state = st_str;
            continue;
        }

        if (state == st_str) {
            state = (c1 != '0') ? st_num : st_zero;
            continue;
        }

        if (state == st_zero && c1 != '0') {
            state = st_frac;
            continue;
        }
    }

    switch (state) {

    case st_str:

        if ((u_char) (c1 - '1') > 8 || (u_char) (c2 - '1') > 8) {
            return diff;
        }

        c1 = *s1++;
        c2 = *s2++;

        /* Fall through. */

    case st_num:

        while (nxt_isdigit(c1) && nxt_isdigit(c2)) {
            c1 = *s1++;
            c2 = *s2++;
        }

        if (nxt_isdigit(c1)) {
            return 1;
        }

        if (nxt_isdigit(c2)) {
            return -1;
        }

        return diff;

    case st_zero:

        if (c1 == '0' || c2 == '\0') {
            return -1;
        }

        if (c2 == '0' || c1 == '\0') {
            return 1;
        }

        /* Fall through. */

    case st_frac:
    default:
        return diff;
    }
}


nxt_bool_t
nxt_strvers_match(u_char *version, u_char *prefix, size_t length)
{
    u_char  next, last;

    if (length == 0) {
        return 1;
    }

    if (nxt_strncmp(version, prefix, length) == 0) {

        next = version[length];

        if (next == '\0') {
            return 1;
        }

        last = version[length - 1];

        if (nxt_isdigit(last) != nxt_isdigit(next)) {
            /* This is a version part boundary. */
            return 1;
        }
    }

    return 0;
}


const uint8_t  nxt_hex2int[256]
    nxt_aligned(32) =
{
    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
     0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 16, 16, 16, 16, 16, 16,
    16, 10, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16,
    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
    16, 10, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16,
    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
};


static const uint32_t  nxt_uri_escape[] = {
    0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */

                /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */
    0xd000002d, /* 1101 0000 0000 0000 0000 0000 0010 1101 */

                /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */
    0x50000000, /* 0101 0000 0000 0000 0000 0000 0000 0000 */

                /*  ~}| {zyx wvut srqp onml kjih gfed cba` */
    0xb8000001, /* 1011 1000 0000 0000 0000 0000 0000 0001 */

    0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
    0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
    0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
    0xffffffff  /* 1111 1111 1111 1111 1111 1111 1111 1111 */
};


u_char *
nxt_decode_uri(u_char *dst, u_char *src, size_t length)
{
    u_char   *end, ch;
    uint8_t  d0, d1;

    nxt_prefetch(&nxt_hex2int['0']);

    end = src + length;

    while (src < end) {
        ch = *src++;

        if (ch == '%') {
            if (nxt_slow_path(end - src < 2)) {
                return NULL;
            }

            d0 = nxt_hex2int[*src++];
            d1 = nxt_hex2int[*src++];

            if (nxt_slow_path((d0 | d1) >= 16)) {
                return NULL;
            }

            ch = (d0 << 4) + d1;
        }

        *dst++ = ch;
    }

    return dst;
}


u_char *
nxt_decode_uri_plus(u_char *dst, u_char *src, size_t length)
{
    u_char   *end, ch;
    uint8_t  d0, d1;

    nxt_prefetch(&nxt_hex2int['0']);

    end = src + length;

    while (src < end) {
        ch = *src++;

        switch (ch) {
        case '%':
            if (nxt_slow_path(end - src < 2)) {
                return NULL;
            }

            d0 = nxt_hex2int[*src++];
            d1 = nxt_hex2int[*src++];

            if (nxt_slow_path((d0 | d1) >= 16)) {
                return NULL;
            }

            ch = (d0 << 4) + d1;
            break;

        case '+':
            ch = ' ';
            break;
        }

        *dst++ = ch;
    }

    return dst;
}


uintptr_t
nxt_encode_uri(u_char *dst, u_char *src, size_t length)
{
    u_char      *end;
    nxt_uint_t  n;

    static const u_char  hex[16] = "0123456789ABCDEF";

    end = src + length;

    if (dst == NULL) {

        /* Find the number of the characters to be escaped. */

        n = 0;

        while (src < end) {

            if (nxt_uri_escape[*src >> 5] & (1U << (*src & 0x1f))) {
                n++;
            }

            src++;
        }

        return (uintptr_t) n;
    }

    while (src < end) {

        if (nxt_uri_escape[*src >> 5] & (1U << (*src & 0x1f))) {
            *dst++ = '%';
            *dst++ = hex[*src >> 4];
            *dst++ = hex[*src & 0xf];

        } else {
            *dst++ = *src;
        }

        src++;
    }

    return (uintptr_t) dst;
}


uintptr_t
nxt_encode_complex_uri(u_char *dst, u_char *src, size_t length)
{
    u_char      *reserved, *end, ch;
    nxt_uint_t  n;

    static const u_char  hex[16] = "0123456789ABCDEF";

    reserved = (u_char *) "?#\0";

    end = src + length;

    if (dst == NULL) {

        /* Find the number of the characters to be escaped. */

        n = 0;

        while (src < end) {
            ch = *src++;

            if (nxt_uri_escape[ch >> 5] & (1U << (ch & 0x1f))) {
                if (ch == reserved[0]) {
                    reserved++;
                    continue;
                }

                if (ch == reserved[1]) {
                    reserved += 2;
                    continue;
                }

                n++;
            }
        }

        return (uintptr_t) n;
    }

    while (src < end) {
        ch = *src++;

        if (nxt_uri_escape[ch >> 5] & (1U << (ch & 0x1f))) {
            if (ch == reserved[0]) {
                reserved++;

            } else if (ch == reserved[1]) {
                reserved += 2;

            } else {
                *dst++ = '%';
                *dst++ = hex[ch >> 4];
                *dst++ = hex[ch & 0xf];
                continue;
            }
        }

        *dst++ = ch;
    }

    return (uintptr_t) dst;
}


nxt_bool_t
nxt_is_complex_uri_encoded(u_char *src, size_t length)
{
    u_char   *reserved, *end, ch;
    uint8_t  d0, d1;

    reserved = (u_char *) "?#\0";

    for (end = src + length; src < end; src++) {
        ch = *src;

        if (nxt_uri_escape[ch >> 5] & (1U << (ch & 0x1f))) {
            if (ch == '%') {
                if (end - src < 2) {
                    return 0;
                }

                d0 = nxt_hex2int[*++src];
                d1 = nxt_hex2int[*++src];

                if ((d0 | d1) >= 16) {
                    return 0;
                }

                continue;
            }

            if (ch == reserved[0]) {
                reserved++;
                continue;
            }

            if (ch == reserved[1]) {
                reserved += 2;
                continue;
            }

            return 0;
        }
    }

    return 1;
}
