/*
 * $Header: /san01/cvs/ashpool/csrc/continuity/mecha/Attic/lst.c,v 1.31 2004/04/30 13:41:56 aleigh Exp $ 
 */

/*
 * Copyright (c) 1994, 2004 Alex Leigh, All Rights Reserved
 * 
 * THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF ALEX LEIGH The copyright
 * notice above does not evidence any actual or intended publication of such
 * source code.
 * 
 * Disclosure, copying, distribution, or transmission of this code without
 * express permission is prohibited.
 */

#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>

#include "mecha.h"

#define FALSE 0
#define TRUE 1

#define STREQ(a, b)              (((*a) == (*b)) && (strcmp((a),(b)) == 0))
#define STRIEQ(a, b)             (strcasecmp((a),(b)) == 0)

#include "lst.h"

/**
 * Initialize a new dynamic array with fixed-size elements. The basic storage is supplied by the caller
 * and must be released by the caller. To release the working memory associated with the dynamic array,
 * lstFarray_free() must be called.
 *
 * The dynamic array implementation is MT-unsafe.
 *
 * @author C. Alexander Leigh
 * @since mk5 mod0
 */
void lstFarray_init(struct lstSarray *array, size_t element_sz) {
    memset(array, 0, sizeof(struct lstSarray));
    array->element_sz = element_sz;
}

/**
 * Add an element to the end of the array. The data specified by <code>data</code> will be copied
 * into the array and is no longer used after the function returns. <code>data</code> may not be
 * <code>NULL</code>.
 *
 * If <code>index</code> is non-NULL, the index position for the newly added element will be returned
 * to the caller.
 *
 * On success, 0 is returned. If a memory allocation failure occurs, the program will abort.
 *
 * @author C. Alexander Leigh
 * @since mk5 mod0
 */
int lstFarray_add(struct lstSarray *array, void *data, size_t *index) {
    int idx;

    assert(array != NULL);
    assert(data!=NULL);

    idx = array->size;
    array->size++;

    if (array->size >= array->maxsize) {
        array->maxsize = array->size * 2;
        // FIXME: Check for realloc() failure here, it's possible
        array->elements = realloc(array->elements, array->element_sz * array->maxsize);
        if(array->elements==NULL) {
            perror("realloc()");
            abort();
        }
    }

    char *p = array->elements + (array->element_sz * idx);

    memcpy(p, data, array->element_sz);

    if(index!=NULL) *index = idx;

    return 0;
}

int lstFarray_set(struct lstSarray *array, size_t idx, const char *data) {
    assert(array!=NULL);
    assert(data!=NULL);

    if(idx>=array->size) return -1;

    char *p = array->elements + (array->element_sz * idx);
    memcpy(p, data, array->element_sz);

    return 0;
}

/**
 * Free the memory associated with this array. Note that the base memory for the array structure is owned by
 * the caller and will not be released by this function.
 *
 * If the caller-supplied <code>freefunc</code> is non-NULL, it will be called with the base memory address
 * for the array and the number of elements stored in the array.
 *
 *  @author C. Alexander Leigh
 *  @since mk15 mod0
 */
void lstFarray_free(struct lstSarray *array, void(* freefunc)(char *, size_t)) {
    assert(array!=NULL);
    if(freefunc!=NULL && array->size>0) {
        freefunc(array->elements, array->size);
    }
    if(array->elements!=NULL) free(array->elements);
}

/**
 * Return a pointer to the element in the array at the given index, or, NULL if no such index is stored
 * in the array.
 *
 * Returns 0 if an element was found, or -1 if the index was out of range.
 *
 * @author C. Alexander Leigh
 * @since mk15 mod0
 */
int lstFarray_get(void **p, struct lstSarray *array, size_t idx) {
    assert(array != NULL);
    if (idx >= array->size)
        return -1;

    *p=array->elements + (array->element_sz * idx);

    return 0;
}

/**
 * Remove the element at the specified position from the array. This works by copying the memory for the back half
 * of the array forward, without changing the size of the allocated memory.
 *
 * @author C. Alexander Leigh
 * @since mk15 mod0
 */
int lstFarray_remove(struct lstSarray *array, size_t idx) {
    // TODO: Implement an efficient case here for removing from the end
    assert(array!=NULL);
    if(idx>=array->size) return -1;

    char *dst = array->elements + (array->element_sz * idx);
    char *src = array->elements + (array->element_sz * (idx + 1));
    size_t len = array->element_sz * (array->size-(idx-1));

    memmove(dst, src, len);

    array->size--;

    return 0;
}

/*** LEGACY ***/

static int lstFset_find_cmp_len(const lstTset *set, const char *key,
                                int key_len, int (*cmp)(const char *s1,
                                                        const char *s2));

void lstFset_bupdate(lstTset *set, const char *key, size_t key_len,
                     const char *value, size_t value_len) {
    assert(set != NULL);
    assert(key != NULL);
    assert(value != NULL);

    lstFset_delete_key(set, key);
    lstFset_badd(set, key, key_len, value, value_len);
}

void lstFset_update(lstTset *set, const char *key, const char *value) {
    assert(set != NULL);
    assert(key != NULL);
    assert(value != NULL);

    lstFset_delete_key(set, key);
    lstFset_add(set, key, value);
}

lstTset *lstFset_init(void) {
    lstTset *s;

    s = (lstTset *) malloc(sizeof(lstTset));

    assert(s != NULL);

    memFclear(s, sizeof(lstTset));

    return s;
}

lstTset *lstFset_create(const char *name) {
    lstTset *s;

    s = (lstTset *) malloc(sizeof(lstTset));

    assert(s != NULL);

    s->size = 0;
    s->maxsize = 10;            /* default set size */

    if (name != NULL)
        s->name = strFcopy(name);

    s->fields = (lstTfield *) malloc(sizeof(lstTfield) * s->maxsize);

    assert(s->fields != NULL);

    memFclear(s->fields, sizeof(lstTfield) * s->maxsize);

    return s;
}

void lstFset_free(lstTset *set) {
    int i;

    if (set != NULL) {
        if (set->fields != NULL) {
            for (i = 0; i < set->size; i++) {
                if (set->fields[i].name != NULL)
                    free(set->fields[i].name);
                if (set->fields[i].value != NULL)
                    free(set->fields[i].value);
            }
            free(set->fields);
        }
        if (set->name != NULL)
            free(set->name);
        free(set);
    }
}

int lstFset_add(lstTset *set, const char *key, const char *value) {
    int idx;

    assert(set != NULL);
    assert(key != NULL);

    if (value == NULL) {
        return -1;
    }
    idx = set->size;
    set->size++;

    if (set->size >= set->maxsize) {
        set->maxsize = set->size * 2;
        set->fields =
                (lstTfield *) realloc(set->fields,
                                      sizeof(lstTfield) * set->maxsize);
    }
    /* initialize values */
    set->fields[idx].n_len = 0;
    set->fields[idx].v_len = 0;

    set->fields[idx].name = strFcopy_size(key, &set->fields[idx].n_len);
    set->fields[idx].value = strFcopy_size(value, &set->fields[idx].v_len);

    return idx;
}

int lstFset_badd(lstTset *set, const char *key, size_t key_len, const char *value, size_t value_len) {
    int idx;

    assert(set != NULL);
    assert(key != NULL);

    if (value == NULL) {
        return -1;
    }
    idx = set->size;
    set->size++;

    if (set->size >= set->maxsize) {
        set->maxsize = set->size * 2;
        set->fields = (lstTfield *) realloc(set->fields, sizeof(lstTfield) * set->maxsize);
    }
    set->fields[idx].name = (char *) memFdup(key, key_len);
    set->fields[idx].n_len = key_len;
    set->fields[idx].value = (char *) memFdup(value, value_len);
    set->fields[idx].v_len = value_len;

    return idx;
}

int lstFset_nadd(lstTset *set, const char *key, int value) {
    char buf[16];

    assert(set != NULL);
    assert(key != NULL);

    snprintf(buf, sizeof(buf), "%d", value);
    return lstFset_add(set, key, buf);
}


int lstFset_unique_cmp(lstTset *set, const char *key,
                       int (*cmp)(const char *s1, const char *s2)) {
    int i;
    char *name;
    int found;

    assert(set != NULL);
    assert(key != NULL);
    assert(cmp != NULL);

    found = 0;
    for (i = 0; i < set->size; ++i) {
        name = set->fields[i].name;
        if ((key == NULL && name == NULL) ||
            (key != NULL && name != NULL && ((*cmp)(key, name)) == 0)) {

            if (found) {
                return FALSE;
            }
            found = 1;
        }
    }
    return TRUE;
}

const char *lstFset_get_index(lstTset *set, int idx) {
    assert(set != NULL);
    if (idx >= set->size)
        return NULL;
    return set->fields[idx].value;
}

const char *lstFset_get_key_index(lstTset *set, int idx) {
    assert(set != NULL);
    if (idx >= set->size)
        return NULL;
    return set->fields[idx].name;
}

int lstFset_find_cmp(const lstTset *set, const char *key,
                     int (*cmp)(const char *s1, const char *s2)) {
    int i;

    assert(set != NULL);
    assert(key != NULL);
    assert(cmp != NULL);

    for (i = 0; i < set->size; ++i) {
        char *name = set->fields[i].name;

        if ((key == NULL && name == NULL) ||
            (key != NULL && name != NULL && ((*cmp)(key, name)) == 0)) {

            return i;
        }
    }

    return -1;
}

static int lstFset_find_cmp_len(const lstTset *set, const char *key,
                                int key_len, int (*cmp)(const char *s1,
                                                        const char *s2)) {
    int i;

    assert(set != NULL);
    assert(key != NULL);
    assert(cmp != NULL);

    for (i = 0; i < set->size; ++i) {
        char *name = set->fields[i].name;
        int name_len = set->fields[i].n_len;

        if ((key == NULL && name == NULL) ||
            (key != NULL && name != NULL &&
             key_len == name_len && ((*cmp)(key, name)) == 0)) {

            return i;
        }
    }

    return -1;
}


const char *lstFset_get_cmp(const lstTset *set, const char *key,
                            int (*cmp)(const char *s1, const char *s2)) {
    int i;

    if (set == NULL)
        return NULL;

    assert(key != NULL);
    assert(cmp != NULL);

    i = lstFset_find_cmp(set, key, cmp);

    if (i == -1)
        return NULL;

    return set->fields[i].value;
}


size_t lstFset_get_vsize_cmp(lstTset *set, const char *key,
                             int (*cmp)(const char *s1, const char *s2)) {
    int i;

    i = lstFset_find_cmp(set, key, cmp);
    if (i == -1)
        return -1;

    return set->fields[i].v_len;
}

size_t lstFset_get_nsize_cmp(lstTset *set, const char *key,
                             int (*cmp)(const char *s1, const char *s2)) {
    int i;

    i = lstFset_find_cmp(set, key, cmp);
    if (i == -1)
        return -1;

    return set->fields[i].n_len;
}

/* Check to see if a key in a set is unique (case sensitive) */
int lstFset_unique(lstTset *set, const char *key) {
    return lstFset_unique_cmp(set, key,
                              (int (*)(const char *, const char *))
                                      strcmp);
}

/* Check if a key in a set is unique (case insensitive) */
int lstFset_inique(lstTset *set, const char *key) {
    return lstFset_unique_cmp(set, key,
                              (int (*)(const char *, const char *))
                                      strcasecmp);
}

/* Locate the idx of a field in a set (sensitive) */
int lstFset_find(lstTset *set, const char *key) {
    return lstFset_find_cmp(set, key,
                            (int (*)(const char *, const char *)) strcmp);
}

/* Locate the idx of a field in a set (insensitive) */
int lstFset_ifind(lstTset *set, const char *key) {
    return lstFset_find_cmp(set, key,
                            (int (*)(const char *, const char *))
                                    strcasecmp);
}

/* Return the value associated with a key, sensitive */
const char *lstFset_get(const lstTset *set, const char *key) {
    return lstFset_get_cmp(set, key,
                           (int (*)(const char *, const char *)) strcmp);
}

ssize_t lstFset_get_vsize(lstTset *set, const char *key) {
    return lstFset_get_vsize_cmp(set, key,
                                 (int (*)(const char *, const char *))
                                         strcmp);
}

ssize_t lstFset_get_nsize(lstTset *set, const char *key) {
    return lstFset_get_nsize_cmp(set, key,
                                 (int (*)(const char *, const char *))
                                         strcmp);
}

const lstTfield *lstFset_get_field(lstTset *set, const char *key,
                                   int key_len) {
    int i;

    if (set == NULL)
        return NULL;

    assert(key != NULL);

    i = lstFset_find_cmp_len(set, key, key_len, strcmp);

    if (i == -1)
        return NULL;

    return ((const lstTfield *) &set->fields[i]);
}

/* Return the value associated with a key, insensitive */
const char *lstFset_iget(lstTset *set, const char *key) {
    return lstFset_get_cmp(set, key,
                           (int (*)(const char *, const char *))
                                   strcasecmp);
}

/* Remove all tuples after 'size' */
void lstFset_trunc(lstTset *set, int size) {
    if (size < set->size) {
        int i;

        for (i = size; i < set->size; i++) {
            free(set->fields[i].name);
            free(set->fields[i].value);
        }
        set->size = size;
    }
}

/* Delete a tuple from a set */
void lstFset_delete(lstTset *set, int idx) {
    if ((idx != -1) && (idx < set->size)) {
        free(set->fields[idx].name);
        free(set->fields[idx].value);

        set->fields[idx].name = set->fields[set->size - 1].name;
        set->fields[idx].n_len = set->fields[set->size - 1].n_len;
        set->fields[idx].value = set->fields[set->size - 1].value;
        set->fields[idx].v_len = set->fields[set->size - 1].v_len;

        set->size--;
    }
}

/* Set the value for a given tuple */
void lstFset_add_value(lstTset *set, int idx, const char *value) {
    if ((idx != -1) && (idx < set->size)) {
        free(set->fields[idx].value);
        if (*value == 0)
            set->fields[idx].value = strFcopy("(null)");
        else
            set->fields[idx].value = strFcopy(value);
    }
}


void lstFset_delete_key(lstTset *set, const char *key) {
    lstFset_delete(set, lstFset_find(set, key));
}

void lstFset_idelete_key(lstTset *set, const char *key) {
    lstFset_delete(set, lstFset_ifind(set, key));
}

lstTset *lstFset_list_find(lstTset **sets, const char *name) {
    while (*sets != NULL) {
        if (name == NULL) {
            if ((*sets)->name == NULL) {
                return (*sets);
            }
        } else {
            if ((*sets)->name != NULL && STREQ((*sets)->name, name)) {

                return (*sets);
            }
        }
        ++sets;
    }
    return NULL;
}

/*
 * You could be outside, right now.
 */

void lstFset_list_free(lstTset *sets) {
    lstTset **s;

    s = (lstTset **) sets;
    while (*s != NULL) {
        lstFset_free((lstTset *) *s);
        s++;
    }
    free(sets);
}

void lstFset_merge(lstTset *high, const lstTset *low) {
    int i, j;

    for (i = 0; i < low->size; ++i) {
        j = lstFset_find(high, low->fields[i].name);
        if (j == -1) {
            lstFset_add(high, low->fields[i].name, low->fields[i].value);
        }
    }
}

/*
 * return the size, in bytes, of all the name and value data without the list
 * overhead.
 */
int lstFset_size(const lstTset *set) {
    int i;
    int size = 0;

    if (set->size == 0)
        return 0;

    /* Calculate the size of the list */
    for (i = 0; i < set->size; i++) {
        if (set->fields[i].value != NULL && set->fields[i].name != NULL) {
            size += strlen(set->fields[i].value);
            size += strlen(set->fields[i].name);
        }
    }

    return size;

}

/*
 * Return the physical idx location of the nth item in the list.
 */
int lstFnth_find(const lstTset *list, int nth) {
    int i;
    int found = -1;

    for (i = 0; i < list->size; i++) {
        if (list->fields[i].name != NULL && list->fields[i].value != NULL)
            found++;
        if (found == nth)
            return i;
    }
    return -1;
}

const char *lstFfield_value(const lstTfield *field) {
    return (field->value);
}

int lstFfield_value_len(const lstTfield *field) {
    return (field->v_len);
}


/********************************************************/

/*
 * This code originates with the TA/Velocity telemetry engine. The packs were
 * used to transfer lists of telemetry elements between hosts, but, they have
 * far more potential general use.
 */


#define TA_INT8 1
#define TA_INT16 2
#define TA_CHAR8 3
#define TA_MARKER 4

struct pckSelem {
    uint16_t len;
    uint8_t class;
};

int lstFpack(char *dest, char *src, int len, int class) {
    char *wp;
    struct pckSelem bar;

    wp = dest;

    memFclear(&bar, sizeof(struct pckSelem));

    bar.len = len;
    bar.class = class;

    memFcopy(&bar, wp, sizeof(struct pckSelem));
    wp += sizeof(struct pckSelem);

    memFcopy(src, wp, len);

    return len + sizeof(struct pckSelem);
}

/*
 * mowin down MC's, like I'm mowin the lawn but now, I'd like to ask you how,
 * you like the feel of the bass in your face in the crowd
 */

void lstFset_dump(char *pack) {
    char *pp;
    struct pckSelem bar;

    pp = pack;

    while (1) {
        memFcopy(pp, &bar, sizeof(struct pckSelem));
        pp += sizeof(struct pckSelem);

        printf("len: %d class: %d\n", bar.len, bar.class);
        if (bar.class == TA_MARKER)
            break;
        pp += bar.len;
    }
}

/*
 * It's not paranoia to think that everyone is out to get you. They are,
 * that's just common sense. Paranoia is thinking that they are conspiring.
 */

lstTset *lstFset_unpack(const char *pack) {
    const char *pp;
    struct pckSelem bar;
    char nbuf[256], vbuf[256];
    lstTset *set;

    pp = pack;

    set = lstFset_create("pack");

    while (1) {
        memFcopy(pp, &bar, sizeof(struct pckSelem));
        if (bar.class == TA_MARKER)
            break;
        pp += sizeof(struct pckSelem);

        memFclear(&nbuf, 256);  /* TODO: lame */
        memFcopy(pp, &nbuf, bar.len);
        pp += bar.len;

        memFcopy(pp, &bar, sizeof(struct pckSelem));
        if (bar.class == TA_MARKER)
            break;
        pp += sizeof(struct pckSelem);

        memFclear(&vbuf, 256);  /* TODO: lame */
        memFcopy(pp, &vbuf, bar.len);
        pp += bar.len;

        lstFset_add(set, (char *) &nbuf, (char *) &vbuf);
    }

    return set;
}

/*
 * Pack a set into a single message for transport. Returns the packed string
 * in allocated space.
 */

char *lstFset_pack(const lstTset *set, size_t *len) {
    char *pack, *pp;
    int i;
    int size;
    int ret;
    struct pckSelem bar;

    assert(set != NULL);

    size = lstFset_size(set);

    if (size == 0)
        return NULL;

    /*
     * The size of the pack is going to be the payload, plus four bytes per
     * tuple
     */

    size += ((sizeof(struct pckSelem) * 2) * set->size);
    size += sizeof(struct pckSelem);    /* For marker */

    /* 4 int 16 per tuple and 2int16 for marker */

    pack = (char *) malloc(size);

    memFclear(pack, size);

    pp = pack;

    for (i = 0; i < set->size; i++) {
        if (set->fields[i].name != NULL && set->fields[i].value != NULL) {
            ret =
                    lstFpack(pp, set->fields[i].name,
                             strlen(set->fields[i].name), TA_CHAR8);
            pp += ret;
            ret =
                    lstFpack(pp, set->fields[i].value,
                             strlen(set->fields[i].value), TA_CHAR8);
            pp += ret;
        }
    }

    /* Insert marker */
    memFclear(&bar, sizeof(struct pckSelem));
    bar.len = 0;
    bar.class = TA_MARKER;
    memFcopy(&bar, pp, sizeof(struct pckSelem));

    *len = size;

    return pack;
}
