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


#include <njs_main.h>


njs_arr_t *
njs_arr_create(njs_mp_t *mp, njs_uint_t n, size_t size)
{
    njs_arr_t  *arr;

    arr = njs_mp_alloc(mp, sizeof(njs_arr_t) + n * size);
    if (njs_slow_path(arr == NULL)) {
        return NULL;
    }

    arr->start = (char *) arr + sizeof(njs_arr_t);
    arr->items = 0;
    arr->item_size = size;
    arr->available = n;
    arr->pointer = 1;
    arr->separate = 1;
    arr->mem_pool = mp;

    return arr;
}


void *
njs_arr_init(njs_mp_t *mp, njs_arr_t *arr, void *start, njs_uint_t n,
    size_t size)
{
    arr->start = start;
    arr->items = n;
    arr->item_size = size;
    arr->available = n;
    arr->pointer = 0;
    arr->separate = 0;
    arr->mem_pool = mp;

    if (arr->start == NULL) {
        arr->separate = 1;
        arr->items = 0;

        arr->start = njs_mp_alloc(mp, n * size);
    }

    return arr->start;
}


void
njs_arr_destroy(njs_arr_t *arr)
{
    if (arr->separate) {
        njs_mp_free(arr->mem_pool, arr->start);
#if (NJS_DEBUG)
        arr->start = NULL;
        arr->items = 0;
        arr->available = 0;
#endif
    }

    if (arr->pointer) {
        njs_mp_free(arr->mem_pool, arr);
    }
}


void *
njs_arr_add(njs_arr_t *arr)
{
    return njs_arr_add_multiple(arr, 1);
}


void *
njs_arr_add_multiple(njs_arr_t *arr, njs_uint_t items)
{
    void      *item, *start, *old;
    uint32_t  n;

    n = arr->available;
    items += arr->items;

    if (items >= n) {

        if (n < 16) {
            /* Allocate new arr twice as much as current. */
            n *= 2;

        } else {
            /* Allocate new arr half as much as current. */
            n += n / 2;
        }

        if (n < items) {
            n = items;
        }

        start = njs_mp_alloc(arr->mem_pool, n * arr->item_size);
        if (njs_slow_path(start == NULL)) {
            return NULL;
        }

        arr->available = n;
        old = arr->start;
        arr->start = start;

        memcpy(start, old, arr->items * arr->item_size);

        if (arr->separate == 0) {
            arr->separate = 1;

        } else {
            njs_mp_free(arr->mem_pool, old);
        }
    }

    item = (char *) arr->start + arr->items * arr->item_size;

    arr->items = items;

    return item;
}


void *
njs_arr_zero_add(njs_arr_t *arr)
{
    void  *item;

    item = njs_arr_add(arr);

    if (njs_fast_path(item != NULL)) {
        njs_memzero(item, arr->item_size);
    }

    return item;
}


void
njs_arr_remove(njs_arr_t *arr, void *item)
{
    u_char    *next, *last, *end;
    uint32_t  item_size;

    item_size = arr->item_size;
    end = (u_char *) arr->start + item_size * arr->items;
    last = end - item_size;

    if (item != last) {
        next = (u_char *) item + item_size;

        memmove(item, next, end - next);
    }

    arr->items--;
}
