summaryrefslogblamecommitdiffhomepage
path: root/src/nxt_var.c
blob: 0a722d17e33c9b0316759a11b127169e1298aeb3 (plain) (tree)
1
2
3
4
5
6
7
8
9








                            

                               
                                          
                               

  

                                    




                








                                 




















                                                          
                                                          
 

                                                                               




































                                                                               














                                           



























































































































                                                                              
                                                              
 







                                          
 






                                                          





                                       


         
                                                                       
 
                                      



                                     
                              
                  
                     
 

                                 
 
                                             
 



                                
          


                     
                                                             


                                            



                                              


                                              

                
         

                 


























































































































































                                                                              






                            
 

                                







                                       
                             










                                                       


                                                                 








                                                  
                                                                   


























































                                                                           






                                        









                                               

                                 

                                         
                                                                   











                                                
                                                   

         
                                                                    








                                       
                                     
 
                 

                                         
                                    
 

                                                           


                                                               

                                         

         
                                  




                                                              


                                       
 

                                                                   







                                                                   

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

#include <nxt_main.h>


struct nxt_var_s {
    size_t              length;
    nxt_uint_t          vars;
    uint8_t             strz;  /* 1 bit */
    u_char              data[];

/*
    nxt_var_sub_t       subs[vars];
    u_char              raw[length];
*/
};


typedef struct {
    uint32_t            index;
    uint32_t            length;
    uint32_t            position;
} nxt_var_sub_t;


typedef struct {
    nxt_var_t           *var;
    nxt_str_t           *value;
} nxt_var_value_t;


struct nxt_var_query_s {
    nxt_array_t         values;   /* of nxt_var_value_t */
    nxt_array_t         parts;    /* of nxt_str_t * */

    nxt_lvlhsh_t        cache;

    nxt_str_t           *spare;
    nxt_uint_t          waiting;
    nxt_uint_t          failed;   /* 1 bit */

    void                *ctx;
    void                *data;

    nxt_work_handler_t  ready;
    nxt_work_handler_t  error;
};


#define nxt_var_subs(var)  ((nxt_var_sub_t *) (var)->data)

#define nxt_var_raw_start(var)                                                \
    ((var)->data + (var)->vars * sizeof(nxt_var_sub_t))


static nxt_int_t nxt_var_hash_test(nxt_lvlhsh_query_t *lhq, void *data);
static nxt_var_decl_t *nxt_var_hash_find(nxt_str_t *name);

static nxt_int_t nxt_var_cache_test(nxt_lvlhsh_query_t *lhq, void *data);
static nxt_str_t *nxt_var_cache_find(nxt_lvlhsh_t *lh, uint32_t index);
static nxt_int_t nxt_var_cache_add(nxt_lvlhsh_t *lh, uint32_t index,
    nxt_str_t *value, nxt_mp_t *mp);

static u_char *nxt_var_next_part(u_char *start, size_t length, nxt_str_t *part,
    nxt_bool_t *is_var);

static void nxt_var_query_finish(nxt_task_t *task, nxt_var_query_t *query);


static const nxt_lvlhsh_proto_t  nxt_var_hash_proto  nxt_aligned(64) = {
    NXT_LVLHSH_DEFAULT,
    nxt_var_hash_test,
    nxt_lvlhsh_alloc,
    nxt_lvlhsh_free,
};

static const nxt_lvlhsh_proto_t  nxt_var_cache_proto  nxt_aligned(64) = {
    NXT_LVLHSH_DEFAULT,
    nxt_var_cache_test,
    nxt_mp_lvlhsh_alloc,
    nxt_mp_lvlhsh_free,
};


static nxt_lvlhsh_t       nxt_var_hash;
static uint32_t           nxt_var_count;

static nxt_var_handler_t  *nxt_var_index;


void
nxt_var_raw(nxt_var_t *var, nxt_str_t *str)
{
    str->length = var->length;
    str->start = nxt_var_raw_start(var);
}


nxt_bool_t
nxt_var_is_const(nxt_var_t *var)
{
    return (var->vars == 0);
}


static nxt_int_t
nxt_var_hash_test(nxt_lvlhsh_query_t *lhq, void *data)
{
    nxt_var_decl_t  *decl;

    decl = data;

    return nxt_strstr_eq(&lhq->key, &decl->name) ? NXT_OK : NXT_DECLINED;
}


static nxt_var_decl_t *
nxt_var_hash_find(nxt_str_t *name)
{
    nxt_lvlhsh_query_t  lhq;

    lhq.key_hash = nxt_djb_hash(name->start, name->length);
    lhq.key = *name;
    lhq.proto = &nxt_var_hash_proto;

    if (nxt_lvlhsh_find(&nxt_var_hash, &lhq) != NXT_OK) {
        return NULL;
    }

    return lhq.value;
}


static nxt_int_t
nxt_var_cache_test(nxt_lvlhsh_query_t *lhq, void *data)
{
    return NXT_OK;
}


static nxt_str_t *
nxt_var_cache_find(nxt_lvlhsh_t *lh, uint32_t index)
{
    nxt_lvlhsh_query_t  lhq;

    lhq.key_hash = nxt_murmur_hash2_uint32(&index);
    lhq.key.length = sizeof(uint32_t);
    lhq.key.start = (u_char *) &index;
    lhq.proto = &nxt_var_cache_proto;

    if (nxt_lvlhsh_find(lh, &lhq) != NXT_OK) {
        return NULL;
    }

    return lhq.value;
}


static nxt_int_t
nxt_var_cache_add(nxt_lvlhsh_t *lh, uint32_t index, nxt_str_t *value,
    nxt_mp_t *mp)
{
    nxt_lvlhsh_query_t  lhq;

    lhq.key_hash = nxt_murmur_hash2_uint32(&index);
    lhq.replace = 0;
    lhq.key.length = sizeof(uint32_t);
    lhq.key.start = (u_char *) &index;
    lhq.value = value;
    lhq.proto = &nxt_var_cache_proto;
    lhq.pool = mp;

    return nxt_lvlhsh_insert(lh, &lhq);
}


nxt_int_t
nxt_var_register(nxt_var_decl_t *decl, size_t n)
{
    nxt_uint_t          i;
    nxt_lvlhsh_query_t  lhq;

    lhq.replace = 0;
    lhq.proto = &nxt_var_hash_proto;

    for (i = 0; i < n; i++) {
        lhq.key = decl[i].name;
        lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length);
        lhq.value = &decl[i];

        if (nxt_slow_path(nxt_lvlhsh_insert(&nxt_var_hash, &lhq) != NXT_OK)) {
            return NXT_ERROR;
        }
    }

    nxt_var_count += n;

    return NXT_OK;
}


nxt_int_t
nxt_var_index_init(void)
{
    nxt_uint_t         i;
    nxt_var_decl_t     *decl;
    nxt_var_handler_t  *index;
    nxt_lvlhsh_each_t  lhe;

    index = nxt_memalign(64, nxt_var_count * sizeof(nxt_var_handler_t));
    if (index == NULL) {
        return NXT_ERROR;
    }

    nxt_lvlhsh_each_init(&lhe, &nxt_var_hash_proto);

    for (i = 0; i < nxt_var_count; i++) {
        decl = nxt_lvlhsh_each(&nxt_var_hash, &lhe);
        decl->index = i;
        index[i] = decl->handler;
    }

    nxt_var_index = index;

    return NXT_OK;
}


nxt_var_t *
nxt_var_compile(nxt_str_t *str, nxt_mp_t *mp, nxt_bool_t strz)
{
    u_char          *p, *end, *next, *src;
    size_t          size;
    nxt_var_t       *var;
    nxt_str_t       part;
    nxt_uint_t      n;
    nxt_bool_t      is_var;
    nxt_var_sub_t   *subs;
    nxt_var_decl_t  *decl;

    n = 0;

    p = str->start;
    end = p + str->length;

    while (p < end) {
        p = nxt_var_next_part(p, end - p, &part, &is_var);
        if (nxt_slow_path(p == NULL)) {
            return NULL;
        }

        if (is_var) {
            n++;
        }
    }

    size = sizeof(nxt_var_t) + n * sizeof(nxt_var_sub_t) + str->length;

    var = nxt_mp_get(mp, size + strz);
    if (nxt_slow_path(var == NULL)) {
        return NULL;
    }

    var->length = str->length;
    var->vars = n;
    var->strz = strz;

    subs = nxt_var_subs(var);
    src = nxt_var_raw_start(var);

    nxt_memcpy(src, str->start, str->length);

    if (strz) {
        src[str->length] = '\0';
    }

    n = 0;
    p = str->start;

    while (p < end) {
        next = nxt_var_next_part(p, end - p, &part, &is_var);

        if (is_var) {
            decl = nxt_var_hash_find(&part);
            if (nxt_slow_path(decl == NULL)) {
                return NULL;
            }

            subs[n].index = decl->index;
            subs[n].length = next - p;
            subs[n].position = p - str->start;

            n++;
        }

        p = next;
    }

    return var;
}


nxt_int_t
nxt_var_test(nxt_str_t *str, u_char *error)
{
    u_char          *p, *end, *next;
    nxt_str_t       part;
    nxt_bool_t      is_var;
    nxt_var_decl_t  *decl;

    p = str->start;
    end = p + str->length;

    while (p < end) {
        next = nxt_var_next_part(p, end - p, &part, &is_var);

        if (next == NULL) {
            nxt_sprintf(error, error + NXT_MAX_ERROR_STR,
                        "Invalid variable at position %uz%Z", p - str->start);

            return NXT_ERROR;
        }

        if (is_var) {
            decl = nxt_var_hash_find(&part);

            if (decl == NULL) {
                nxt_sprintf(error, error + NXT_MAX_ERROR_STR,
                            "Unknown variable \"%V\"%Z", &part);

                return NXT_ERROR;
            }
        }

        p = next;
    }

    return NXT_OK;
}


static u_char *
nxt_var_next_part(u_char *start, size_t length, nxt_str_t *part,
    nxt_bool_t *is_var)
{
    u_char      *p, *end, ch, c;
    nxt_bool_t  bracket;

    end = start + length;

    p = nxt_memchr(start, '$', length);

    if (p == start) {
        *is_var = 1;

        p++;

        if (p == end) {
            return NULL;
        }

        if (*p == '{') {
            bracket = 1;

            if (end - p < 2) {
                return NULL;
            }

            p++;

        } else {
            bracket = 0;
        }

        start = p;

        for ( ;; ) {
            ch = *p;

            c = (u_char) (ch | 0x20);
            if ((c < 'a' || c > 'z') && ch != '_') {

                if (bracket && ch != '}') {
                    return NULL;
                }

                break;
            }

            p++;

            if (p == end) {
                if (bracket) {
                    return NULL;
                }

                break;
            }
        }

        length = p - start;
        end = p + bracket;

    } else {
        *is_var = 0;

        if (p != NULL) {
            length = p - start;
            end = p;
        }
    }

    part->length = length;
    part->start = start;

    return end;
}


nxt_int_t
nxt_var_query_init(nxt_var_query_t **query_p, void *ctx, nxt_mp_t *mp)
{
    nxt_var_query_t  *query;

    query = *query_p;

    if (*query_p == NULL) {
        query = nxt_mp_zget(mp, sizeof(nxt_var_query_t));
        if (nxt_slow_path(query == NULL)) {
            return NXT_ERROR;
        }

        nxt_array_init(&query->values, mp, sizeof(nxt_var_value_t));
        nxt_array_init(&query->parts, mp, sizeof(nxt_str_t *));

    } else {
        nxt_array_reset(&query->values);
    }

    query->ctx = ctx;

    *query_p = query;

    return NXT_OK;
}


void
nxt_var_query(nxt_task_t *task, nxt_var_query_t *query, nxt_var_t *var,
    nxt_str_t *str)
{
    uint32_t         index;
    nxt_mp_t         *mp;
    nxt_str_t        *value;
    nxt_int_t        ret;
    nxt_uint_t       i;
    nxt_var_sub_t    *subs;
    nxt_var_value_t  *val;

    if (nxt_var_is_const(var)) {
        nxt_var_raw(var, str);
        return;
    }

    if (nxt_slow_path(query->failed)) {
        return;
    }

    mp = query->values.mem_pool;
    subs = nxt_var_subs(var);
    value = query->spare;

    for (i = 0; i < var->vars; i++) {

        if (value == NULL) {
            value = nxt_mp_zget(mp, sizeof(nxt_str_t));
            if (nxt_slow_path(value == NULL)) {
                goto fail;
            }
        }

        index = subs[i].index;

        ret = nxt_var_cache_add(&query->cache, index, value, mp);

        if (ret != NXT_OK) {
            if (nxt_slow_path(ret == NXT_ERROR)) {
                goto fail;
            }

            continue;  /* NXT_DECLINED */
        }

        ret = nxt_var_index[index](task, query, value, query->ctx);

        value = NULL;

        if (ret != NXT_OK) {
            if (nxt_slow_path(ret != NXT_AGAIN)) {
                goto fail;
            }

            query->waiting++;
        }
    }

    query->spare = value;

    val = nxt_array_add(&query->values);
    if (nxt_slow_path(val == NULL)) {
        goto fail;
    }

    val->var = var;
    val->value = str;

    return;

fail:

    query->failed = 1;
}


void
nxt_var_query_resolve(nxt_task_t *task, nxt_var_query_t *query, void *data,
    nxt_work_handler_t ready, nxt_work_handler_t error)
{
    query->data = data;
    query->ready = ready;
    query->error = error;

    if (query->waiting == 0) {
        nxt_var_query_finish(task, query);
    }
}


void
nxt_var_query_handle(nxt_task_t *task, nxt_var_query_t *query,
    nxt_bool_t failed)
{
    query->failed |= failed;

    if (--query->waiting == 0) {
        nxt_var_query_finish(task, query);
    }
}


static void
nxt_var_query_finish(nxt_task_t *task, nxt_var_query_t *query)
{
    u_char           *p, *src;
    size_t           length, last, next;
    nxt_str_t        *str, **part;
    nxt_var_t        *var;
    nxt_uint_t       i, j;
    nxt_var_sub_t    *subs;
    nxt_var_value_t  *val;

    if (query->failed) {
        goto done;
    }

    val = query->values.elts;

    for (i = 0; i < query->values.nelts; i++) {
        var = val[i].var;

        subs = nxt_var_subs(var);
        length = var->length;

        for (j = 0; j < var->vars; j++) {
            str = nxt_var_cache_find(&query->cache, subs[j].index);

            nxt_assert(str != NULL);

            part = nxt_array_add(&query->parts);

            if (nxt_slow_path(part == NULL)) {
                query->failed = 1;
                goto done;
            }

            *part = str;

            length += str->length - subs[j].length;
        }

        p = nxt_mp_nget(query->values.mem_pool, length + var->strz);
        if (nxt_slow_path(p == NULL)) {
            query->failed = 1;
            goto done;
        }

        val[i].value->length = length;
        val[i].value->start = p;

        part = query->parts.elts;
        src = nxt_var_raw_start(var);

        last = 0;

        for (j = 0; j < var->vars; j++) {
            next = subs[j].position;

            if (next != last) {
                p = nxt_cpymem(p, &src[last], next - last);
            }

            p = nxt_cpymem(p, part[j]->start, part[j]->length);

            last = next + subs[j].length;
        }

        if (last != var->length) {
            p = nxt_cpymem(p, &src[last], var->length - last);
        }

        if (var->strz) {
            *p = '\0';
        }

        nxt_array_reset(&query->parts);

        nxt_debug(task, "var: \"%*s\" -> \"%V\"", var->length, src,
                  val[i].value);
    }

done:

    nxt_work_queue_add(&task->thread->engine->fast_work_queue,
                       query->failed ? query->error : query->ready,
                       task, query->ctx, query->data);
}