sima/autoconf/
sima/hosts/i386/
sima/mudlib/
sima/mudlib/kernel/
sima/mudlib/obj/
sima/mudlib/sys/
sima/synhash/mips/
/* Copyright 1995, 1997 J"orn Rennecke */

#include <stdio.h>

#include "common.h"
#include "alloc.h"

#define PHASHSTRING(str, len) (aphash(str, len) & ((STRTABLE_SIZE)-1))

struct searchstr {
    struct searchstr *next;
    char block[4];
};

struct searchlstr {
    struct searchlstr *next;
    uint32 len;
    char block[4];
};

struct searchstr *str_table[STRTABLE_SIZE];
struct searchlstr *lstr_table[LSTRTABLE_SIZE];

struct short_string nil_string = { T_STRING, 1, 0, /*len*/ 0, 0 };

static char print_buf
 [P_INT_PRINT_SIZE > DOUBLE_PRINT_SIZE ? P_INT_PRINT_SIZE : DOUBLE_PRINT_SIZE];

void init_global_strings() {
    int i;
    struct searchstr **p;
    struct searchlstr **lp;

    i = NELEM(str_table);
    p = str_table;
    do {
	*p = (struct searchstr *)p;
	p++;
    } while(--i);
    i = NELEM(lstr_table);
    lp = lstr_table;
    do {
	*lp = (struct searchlstr *)lp;
	lp++;
    } while(--i);
    make_string_global(NIL_STRING);
    /* We want to be able to use NIL_STRING without concern about ref counts. */
    SV_STRREF(NIL_STRING) = 255;
}

union svalue make_string(register char *str, mp_int length) {
    svalue res;
    register char *dest;

    if (length > MAX_SMALL_STRING) {
        res = ALLOC_LSTRING(length);
        if (!res.p)
            goto out_of_memory;
        dest = SV_LSTRING(res);
        SV_LSTRREF(res) = 1;
        SV_LSTRLEN(res) = length;
    } else {
        res = ALLOC_STRING(length);
        if (!res.p) {
  out_of_memory:
            error(IE_NOMEM);
            return SV_NULL;
        }
        dest = SV_STRING(res);
        SV_STRREF(res) = 0;
        SV_STRLEN(res) = length;
    }
    memcpy(dest, str, length);
    return res;
}

char *sv_string(union svalue sv, mp_uint *lenp) {
    if (!SV_STRING_IS_LONG(sv)) {
	*lenp = SV_STRLEN(sv);
	return SV_STRING(sv);
    } else {
	*lenp = SV_LSTRLEN(sv);
	return SV_LSTRING(sv);
    }
}

struct counted_string sv_string2(union svalue sv) {
    struct counted_string ret;

    if (!SV_STRING_IS_LONG(sv)) {
	ret.len = SV_STRLEN(sv);
	ret.start =  SV_STRING(sv);
	return ret;
    } else {
	ret.len = SV_LSTRLEN(sv);
	ret.start = SV_LSTRING(sv);
	return ret;
    }
}

union svalue findstring(union svalue s) {
    char *str;
    mp_int len;
    int h;

    switch (SV_TYPE(s)) {
      case T_ISTRING:
      case T_ILSTRING:
	s = SV_ISTRING(s);
      case T_GSTRING:
      case T_GLSTRING:
	return s;
      case T_LSTRING:
      {
	struct searchlstr **anchor, *first, *curr, *prev;

	len = SV_LSTRLEN(s);
	str = SV_LSTRING(s);
	h = PHASHSTRING((char *)str, len);
	prev = 0;
	anchor = &lstr_table[h];
	curr = first = *anchor;
	while (&curr->next != anchor) {
	    adtstat[SHS_LSEARCH]++;
	    if (curr->len == len && !memcmp(curr->block, str, len)) {
		if (prev) {
		    adtstat[SHS_LREORDER]++;
		    prev->next = curr->next;
		    curr->next = first;
		    *anchor = curr;
		    SV_TYPE_LOC(s) += T_ILSTRING - T_LSTRING;
		    return SV_ILSTRING(s) = (uint8 *)curr - sizeof(p_int) + 1;
		}
		adtstat[SHS_LHEADMATCH]++;
		return (uint8 *)curr - sizeof(p_int) + 1;
	    }
	    prev = curr;
	    curr = prev->next;
	}
	adtstat[SHS_LFAIL]++;
	((uint8 *)str)[-8 - sizeof curr] = T_GLSTRING;
	((struct searchlstr *)(void *)&str[4])[-1].next = first;
	*anchor = &((struct searchlstr *)(void *)&str[4])[-1];
	return s;
      }
      default:
#ifdef DEBUG
	fatal("Bad string type\n");
#endif
      case T_STRING:
      {
	struct searchstr **anchor, *first, *curr, *prev;

	len = SV_STRLEN(s);
	str = SV_STRING(s);
	h = PHASHSTRING((char *)str, len);
	prev = 0;
	anchor = &str_table[h];
	curr = first = *anchor;
	while (&curr->next != anchor) {
	    adtstat[SHS_SEARCH]++;
	    if (((uint8 *)curr)[-1] == len && !memcmp(curr->block, str, len)) {
		if (prev) {
		    adtstat[SHS_REORDER]++;
		    prev->next = curr->next;
		    curr->next = first;
		    *anchor = curr;
		    SV_TYPE_LOC(s) += T_ISTRING - T_STRING;
		    return SV_ISTRING(s) = (uint8 *)curr - sizeof(p_int) + 1;
		}
		adtstat[SHS_HEADMATCH]++;
		return (uint8 *)curr - sizeof(p_int) + 1;
	    }
	    prev = curr;
	    curr = curr->next;
	}
	adtstat[SHS_FAIL]++;
	((uint8 *)str)[-sizeof(p_int) - sizeof curr] = T_GSTRING;
	((struct searchstr *)(void *)&str[4])[-1].next = first;
	*anchor = &((struct searchstr *)(void *)&str[4])[-1];
	return s;
      }
    }
}

union svalue make_string_global(union svalue s) {
    union svalue s2;

    s2 = findstring(s); /* hand-inline this call when things stabilize */
    if (s2.p != s.p) {
	if (!++SV_REF(s2))
	    s2 = ref_inc(s2);
	FREE_ALLOCED_SVALUE(s);
    }
    return s2;
}

union svalue make_global_string(char *str, mp_int length) {
    union svalue sv = make_string(str, length);
    if (sv.i)
	sv = make_string_global(sv);
    return sv;
}

svalue unshare_string(svalue s) {
    struct searchstr **search, *curr, *prev;

    adtstat[SHS_UNSHARE]++;
    SV_TYPE_LOC(s) += T_STRING - T_GSTRING;
    search = &SV_STRNXT(s);
    curr = *search;
    do {
	prev = curr;
	curr = curr->next;
    } while(&curr->next != search);
    prev->next = curr->next;
    return s;
}

#if 0
void free_string(union svalue s) {
    struct searchstr **search, *curr, *prev;

    adtstat[SHS_FREE]++;
    search = &SV_STRNXT(s);
    curr = *search;
    do {
	prev = curr;
	curr = curr->next;
    } while(&curr->next != search);
    prev->next = curr->next;
    free_block(
	(uint8 *)search - 3,
	sizeof(uint16 *) + (((uint8 *)search)[-1]+3 & ~3)
    );
}

void free_lstring(union svalue s) {
    struct searchlstr **search, *curr, *prev;

    adtstat[SHS_LFREE]++;
    search = &SV_LSTRNXT(s);
    curr = *search;
    do {
	prev = curr;
	curr = curr->next;
    } while(&curr->next != search);
    prev->next = curr->next;
    free_block(
	(uint8 *)search - 3,
	sizeof(uint16 *) + (((struct searchlstr *)search)->len+3 & ~3)
    );
}
#endif

p_int sv_strcmp(svalue sv0, svalue sv1) {
    struct counted_string str0 = sv_string2(sv0);
    struct counted_string str1 = sv_string2(sv1);
    int len = str0.len > str1.len ? str0.len : str1.len;
    int diff = strncmp(str0.start, str1.start, len);
    return diff ? diff : str0.len - str1.len;
}

svalue add_string(svalue sv0, svalue sv1) {
    struct counted_string str0, str1;
    mp_int total;
    svalue res;
    char *dest;

    if (!SV_IS_NUMBER(sv1)) {
	if (SV_IS_STRING(sv1)) {
	    if (!SV_STRING_IS_LONG(sv1)) {
		str1.len = SV_STRLEN(sv1);
		str1.start =  SV_STRING(sv1);
	    } else {
		str1.len = SV_LSTRLEN(sv1);
		str1.start = SV_LSTRING(sv1);
	    }
	} else if (SV_TYPE(sv1) == T_FLOAT) {
	    sprintf(print_buf, "%g", SV_FLOAT(sv1));
	    str1.start = print_buf;
	    str1.len = strlen(print_buf);
	} else if (SV_TYPE(sv1) == T_DESTRUCTED) {
	    str1.start = "0";
	    str1.len = 1;
	} else {
	    FREE_ALLOCED_SVALUE(sv0);
	    bad_efun_arg(2);
	    return sv1;
	}
    } else {
        sprintf(print_buf, "%ld", sv1.i >> 1);
	str1.start = print_buf;
	str1.len = strlen(print_buf);
	sv1 = NIL_STRING;
    }
    if (!SV_STRING_IS_LONG(sv0)) {
	str0.len = SV_STRLEN(sv0);
	str0.start =  SV_STRING(sv0);
    } else {
	str0.len = SV_LSTRLEN(sv0);
	str0.start = SV_LSTRING(sv0);
    }
    total = str0.len + str1.len;

    if (total > MAX_SMALL_STRING) {
	if (SV_TYPE(sv1) == T_LSTRING && SV_REF(sv1) == 1 &&
	  SV_LSTRREF(sv1) == 1 && /* FIXME: test enough free space. */ 0) {
	    res = sv1;
	    dest = str0.start;
	    goto copy_str1;
	}
        res = ALLOC_LSTRING(total);
        if (!res.p)
            goto out_of_memory;
        dest = SV_LSTRING(res);
        SV_LSTRREF(res) = 1;
        SV_LSTRLEN(res) = total;
    } else {
        res = ALLOC_STRING(total);
        if (!res.p) {
  out_of_memory:
            error(IE_NOMEM);
	    FREE_ALLOCED_SVALUE(sv1);
            return sv0;
        }
        dest = SV_STRING(res);
        SV_STRREF(res) = 0;
        SV_STRLEN(res) = total;
    }
    memcpy(dest, str0.start, str0.len);
    dest += str0.len;
    FREE_ALLOCED_SVALUE(sv0);
  copy_str1:
    memcpy(dest, str1.start, str1.len);
    FREE_ALLOCED_SVALUE(sv1);
    return res;
}

svalue *f_strstr(svalue *sp) {
    struct counted_string str0, str1;
    svalue sv0, sv1, sv2 = *sp;
    p_int offset;
    char *found;

    if (!SV_IS_NUMBER(sv2)) {
	bad_efun_arg(3);
	return sp;
    }
    sv1 = *--sp;
    if (!SV_IS_STRING(sv1)) {
	bad_efun_arg(2);
	return sp;
    }
    sv0 = *--sp;
    if (!SV_IS_STRING(sv0)) {
	bad_efun_arg(1);
	return sp;
    }
    str0 = sv_string2(sv0);
    offset = sv2.i >> 1;
    if (offset < 0) {
	offset += str0.len;
	if (offset < 0)
	    offset = 0;
    }
    if (offset > str0.len) {
	found = 0;
    } else {
	str0.start += offset;
	str1 = sv_string2(sv1);
	found = memmem(str1.start, str1.len, str0.start, str0.len - offset);
    }
    *sp = INT_SVALUE(found ? found - str0.start : -1);
    FREE_ALLOCED_SVALUE(sv0);
    FREE_ALLOCED_SVALUE(sv1);
    return sp;
}