/* This file is generated by "mtxrun --script "mtx-wtoc.lua" from the metapost cweb files. */


# include "mpconfig.h"
# include "mpstrings.h"

# define STRCMP_RESULT(a) ((a) < 0 ? -1 : ((a) > 0 ? 1 : 0))

static int mp_aux_comp_strings_entry(void *p, const void *pa, const void *pb)
{
    const mp_lstring *a = (const mp_lstring *) pa;
    const mp_lstring *b = (const mp_lstring *) pb;
    unsigned char *s = a->str;
    unsigned char *t = b->str;
    size_t l = a->len <= b->len ? a->len : b->len;
    (void) p;
    while (l-- > 0) {
        if (*s != *t) {
           return STRCMP_RESULT(*s - *t);
        } else {
            s++;
            t++;
        }
    }
    return STRCMP_RESULT((int)(a->len - b->len));
}

void *mp_aux_copy_strings_entry(const void *p)
{
    mp_string ff = mp_memory_allocate(sizeof(mp_lstring));
    if (ff) {
        const mp_lstring *fp = (const mp_lstring *) p;
        ff->str = mp_memory_allocate((size_t) fp->len + 1);
        if (ff->str) {
            memcpy((char *) ff->str, (char *) fp->str, fp->len + 1);
            ff->len = fp->len;
            ff->refs = 0;
            return ff;
        }
    }
    return NULL;
}

static void *delete_strings_entry(void *p)
{
    mp_string ff = (mp_string) p;
    mp_memory_free(ff->str);
    mp_memory_free(ff);
    return NULL;
}

static mp_string new_strings_entry(void)
{
    mp_string ff = mp_memory_allocate(sizeof(mp_lstring));
    ff->str = NULL;
    ff->len = 0;
    ff->refs = 0;
    return ff;
}

char *mp_strdup(const char *s)
{
    if (s) {
        char *w = lmt_memory_strdup(s);
        if (w) {
            return w;
        } else {
            printf("mplib ran out of memory, case 3");
            exit(EXIT_FAILURE);
        }
    }
    return NULL;
}


char *mp_strndup(const char *p, size_t l)
{
    if (p) {
        char *r = mp_memory_allocate(l * sizeof(char) + 1);
        if (r) {
            char *s = memcpy(r, p, l);
            *(s + l) = '\0';
            return s;
        } else {
            printf("mplib ran out of memory, case 4");
            exit(EXIT_FAILURE);
        }
    }
    return NULL;
}


int mp_strcmp(const char *a, const char *b)
{
    return a == NULL ? (b == NULL ? 0 : -1) : (b == NULL ? 1 : strcmp(a, b));
}

void mp_initialize_strings(MP mp)
{
    mp->strings = avl_create(mp_aux_comp_strings_entry, mp_aux_copy_strings_entry, delete_strings_entry, mp_memory_allocate, mp_memory_free, NULL);
    mp->cur_string = NULL;
    mp->cur_length = 0;
    mp->cur_string_size = 0;
}

void mp_dealloc_strings(MP mp)
{
    if (mp->strings != NULL) {
        avl_destroy(mp->strings);
    } else {
        mp->strings = NULL;
        mp_memory_free(mp->cur_string);
        mp->cur_string = NULL;
        mp->cur_length = 0;
        mp->cur_string_size = 0;
    }
}

char *mp_str(MP mp, mp_string ss)
{
    (void) mp;
    return (char *) ss->str;
}

mp_string mp_rtsl(MP mp, const char *s, size_t l)
{
    mp_string nstr;
    mp_string str = new_strings_entry();
    str->str = (unsigned char *) mp_strndup(s, l);
    str->len = l;
    nstr = (mp_string) avl_find(str, mp->strings);
    if (nstr == NULL) {
        avl_ins(str, mp->strings, avl_false);
        nstr = (mp_string) avl_find(str, mp->strings);
    }
    delete_strings_entry(str);
    add_str_ref(nstr);
    return nstr;
}

mp_string mp_rts(MP mp, const char *s)
{
    return mp_rtsl(mp, s, strlen(s));
}

# define EXTRA_STRING 500

void mp_str_room(MP mp, int wsize)
{
    if ((mp->cur_length + (size_t) wsize + 1) > mp->cur_string_size) {
        size_t nsize = mp->cur_string_size + mp->cur_string_size / 5 + EXTRA_STRING;
        if (nsize < (size_t) wsize) {
            nsize = (size_t) wsize + EXTRA_STRING;
        }
        mp->cur_string = (unsigned char *) mp_memory_reallocate(mp->cur_string, (size_t) nsize * sizeof(unsigned char));
        memset(mp->cur_string + mp->cur_length, 0, nsize-mp->cur_length);
        mp->cur_string_size = nsize;
    }
}

void mp_append_char(MP mp, unsigned char c)
{
    *(mp->cur_string + mp->cur_length) = c;
    mp->cur_length++;
}

void mp_append_str(MP mp, const char *s)
{
    int j = 0;
    while ((unsigned char) s[j]) {
        *(mp->cur_string + mp->cur_length) = s[j++];
        mp->cur_length++;
    }
}

void mp_reset_cur_string(MP mp)
{
    mp_memory_free(mp->cur_string);
    mp->cur_length = 0;
    mp->cur_string_size = 63;
    mp->cur_string = (unsigned char *) mp_memory_allocate(64 * sizeof(unsigned char));
    memset(mp->cur_string, 0, 64);
}

void mp_flush_string(MP mp, mp_string s) {
    if (s->refs == 0) {
        mp->strs_in_use--;
        mp->pool_in_use = mp->pool_in_use - (int) s->len;
        avl_del(s, mp->strings, NULL);
    }
}

mp_string mp_intern(MP mp, const char *s)
{
    mp_string r = mp_rts(mp, s);
    r->refs = MAX_STR_REF;
    return r;
}

mp_string mp_make_string(MP mp)
{
    mp_string str;
    mp_lstring tmp;
    tmp.str = mp->cur_string;
    tmp.len = mp->cur_length;
    str = (mp_string) avl_find(&tmp, mp->strings);
    if (str == NULL) {
        str = mp_memory_allocate(sizeof(mp_lstring));
        str->str = mp->cur_string;
        str->len = tmp.len;
        avl_ins(str, mp->strings, avl_false);
        str = (mp_string) avl_find(&tmp, mp->strings);
        mp->pool_in_use = mp->pool_in_use + (int) str->len;
        if (mp->pool_in_use > mp->max_pl_used) {
            mp->max_pl_used = mp->pool_in_use;
        }
        mp->strs_in_use++;
        if (mp->strs_in_use > mp->max_strs_used) {
            mp->max_strs_used = mp->strs_in_use;
        }
    }
    add_str_ref(str);
    mp_reset_cur_string (mp);
    return str;
}

int mp_str_vs_str(MP mp, mp_string s, mp_string t)
{
    (void) mp;
    return mp_aux_comp_strings_entry(NULL, (const void *) s, (const void *) t);
}

mp_string mp_cat(MP mp, mp_string a, mp_string b)
{
    mp_string str;
    size_t saved_cur_length = mp->cur_length;
    unsigned char *saved_cur_string = mp->cur_string;
    size_t saved_cur_string_size = mp->cur_string_size;
    size_t needed = a->len + b->len;
    mp->cur_length = 0;
    mp->cur_string = (unsigned char *) mp_memory_allocate((size_t) (needed + 1) * sizeof(unsigned char));
    mp->cur_string_size = 0;
    mp_str_room(mp, (int) needed + 1);
    memcpy(mp->cur_string, a->str, a->len);
    memcpy(mp->cur_string + a->len, b->str, b->len);
    mp->cur_length = needed;
    mp->cur_string[needed] = '\0';
    str = mp_make_string(mp);
    mp_memory_free(mp->cur_string);
    mp->cur_length = saved_cur_length;
    mp->cur_string = saved_cur_string;
    mp->cur_string_size = saved_cur_string_size;
    return str;
}

mp_string mp_chop_string(MP mp, mp_string s, int a, int b)
{
    int l = (int) s->len;
    int reversed;
    if (a <= b) {
        reversed = 0;
    } else {
        int k = a;
        a = b;
        b = k;
        reversed = 1;
    }
    if (a < 0) {
        a = 0;
        if (b < 0) {
            b = 0;
        }
    }
    if (b > l) {
        b = l;
        if (a > l) {
            a = l;
        }
    }
    mp_str_room(mp, (b - a));
    if (reversed) {
        for (int k = b - 1; k >= a; k--) {
            mp_append_char(mp, *(s->str + k));
        }
    } else {
        for (int k = a; k < b; k++) {
            mp_append_char(mp, *(s->str + k));
        }
    }
    return mp_make_string(mp);
}
