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 "common.h"
#include "alloc.h"
#include "object.h"
#include "exec.h"
#include "interpret.h"
#include "uid.h"
#include "schedule.h"

p_int num_objects;
union svalue master_ob, simul_efun_ob;

struct object nil_object = {
  T_OBJECT, 1/* ref */, 0/*flags*/, {0/* next.hash */}, {0}, {0},
  {&nil_uid}
};

char **otable[OTABLE_SIZE];

uint8 *language_suffix[] = {".c"}, language_suffix_length[] = {1};

union svalue obj_list_destructed;

void init_otable() {
    char ***p = otable;
    do {
	*p = (char **)p;
    } while (++p != &otable[OTABLE_SIZE]);
    master_ob = find_object(master_name, 1);
    if (!master_ob.p)
	fatal("Failed to load master object\n");
    call_hook(boot_fun, master_ob, 0);
}

union svalue split_basename(uint8 *basestart, mp_uint *lenp) {
    mp_uint baselen;
    uint32 clone;
    int i;

    baselen = *lenp;
    i = 9;
    if (i <= baselen || (i = baselen)) {
	char *end, *zero, c;

	clone = 0;
	end = (char *)&basestart[baselen];
	zero = end;
	do {
	    c = *--end;
	    switch(c) {
	      case '#':
	      {
		if (zero != &end[1]) {
		    *lenp = end - (char *)basestart;
		    return (union svalue)(p_int)(clone << 1);
		}
		break;
	      }
	      case '0':
		zero = end;
			case '1': case '2': case '3': case '4':
	      case '5': case '6': case '7': case '8': case '9':
		clone = (clone << 4) + c - '0';
		continue;
	      case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
		clone = (clone << 4) + c - ('a' - 10);
		continue;
	    }
	    break;
	} while (--i);
    }
    return SV_NULL;
}

#define NXT_TO_OB(p) ( (struct object *)(void *)\
	((char *)p - offsetof(struct object, next.hash) ) )

int enter_object_hash(struct object *ob) {
    union svalue dirname, basename;
    uint8 *basestart, *dirstart;
    mp_uint baselen, dirlen;
    uint32 clone;
    mp_uint hash;
    char ***anchor, **curr;

    dirname = ob->dirname;
    if (dirname.i & 1) {
	basename = ob->basename;
	basestart = sv_string(basename, &baselen);
	clone = split_basename(basestart, &baselen).i;
    } else {
	struct program *prog;

	prog = (struct program *)dirname.p;
	basename = prog->basename;
	dirname = prog->dirname;
	basestart = sv_string(basename, &baselen);
	clone = ob->basename.i;
    }
    dirstart = sv_string(dirname, &dirlen);
    hash = uhash(dirstart, dirlen);
    hash ^= uhash(basestart, baselen);
    hash ^= clone;
    hash ^= hash / (OTABLE_SIZE*OTABLE_SIZE);
    hash ^= hash / OTABLE_SIZE;
    hash &= OTABLE_SIZE - 1;
    anchor = &otable[hash];
    ob->next.hash = curr = *anchor;
    while (curr != (char **)anchor) {
	union svalue curr_dirname, curr_basename;
	uint32 curr_clone;
	uint8 *curr_dirstart, *curr_basestart;
	mp_uint curr_dirlen, curr_baselen;

	curr_dirname = NXT_TO_OB(curr)->dirname;
	if (curr_dirname.i & 1) {
	    curr_basename = NXT_TO_OB(curr)->basename;
	    curr_basestart = sv_string(curr_basename, &curr_baselen);
	    curr_clone = split_basename(curr_basestart, &curr_baselen).i;
	} else {
	    struct program *prog;

	    prog = (struct program *)curr_dirname.p;
	    curr_basename = prog->basename;
	    curr_dirname = prog->dirname;
	    curr_basestart = sv_string(basename, &baselen);
	    curr_clone = NXT_TO_OB(curr)->basename.i;
	}
	if (SV_STR_IS_LONG(curr_dirname)) {
	    curr_dirstart = SV_LSTRING(curr_dirname);
	    curr_dirlen = SV_LSTRLEN(curr_dirname);
	} else {
	    curr_dirstart = SV_STRING(curr_dirname);
	    curr_dirlen = SV_STRLEN(curr_dirname);
	}
	if (curr_dirlen == dirlen && curr_baselen == baselen &&
	    curr_clone == clone &&
	    !memcmp(curr_dirstart, dirstart, dirlen) &&
	    !memcmp(curr_basestart, basestart, baselen) )
	{
	    return 0;
	}
	curr = *(char ***)curr;
    }
    *anchor = (char **)&ob->next.hash;
    return 1;
}

void enter_clone_hash(union svalue sv_ob, union svalue dir, union svalue base)
{
    static p_int clone_counter;

    struct object *ob;
    struct counted_string dirstr;
    char *basestr_start;
    mp_uint  basestr_len;
    mp_uint hash;
    char ***anchor, **curr;

    ob = &SV_OBJECT(sv_ob);
reroll:
    /*
     * We don't expect to cycle this loop more than 1.00001 on average.
     * Using a proper loop statement would send the wrong message to the
     * optimizer.
     */
    if ( !(ob->basename.i = hash = clone_counter += 2) )
	goto reroll;
    dirstr = sv_string2(dir);
    hash ^= ahash(dirstr.start, dirstr.len);
    SV_COUNT_STRING(base, basestr_start, basestr_len);
    hash ^= ahash(basestr_start, basestr_len);
    hash ^= hash / (OTABLE_SIZE*OTABLE_SIZE);
    hash ^= hash / OTABLE_SIZE;
    hash &= OTABLE_SIZE - 1;
    anchor = &otable[hash];
    ob->next.hash = curr = *anchor;
    while (curr != (char **)anchor) {
	union svalue curr_dirname, curr_basename;

	curr_dirname = NXT_TO_OB(curr)->dirname;
	if ( !(curr_dirname.i & 1) ) {
	    struct program *prog;

	    prog = (struct program *)curr_dirname.p;
	    if (prog->dirname.p != dir.p || prog->basename.p != base.p)
		continue;
	    if (NXT_TO_OB(curr)->basename.i != clone_counter)
		continue;
	} else {
	    uint8 *curr_basestart;
	    mp_uint curr_baselen;

	    if (curr_dirname.p != dir.p)
		continue;
	    curr_basestart = sv_string(curr_basename, &curr_baselen);
	    if (clone_counter != split_basename(curr_basestart, &curr_baselen).i)
		continue;
	    if (curr_baselen != basestr_len)
		continue;
	    if (memcmp(curr_basestart, basestr_start, basestr_len))
		continue;
	}
	goto reroll;
    }
    *anchor = (char **)&ob->next.hash;
}

static union svalue clone_name(union svalue base, union svalue clone) {
    struct counted_string basestr;
    mp_int newlen;
    char *dest;
    int32 bits;

    basestr = sv_string2(base);
    bits = clone.i | clone.i >> 1;
    bits |= bits >> 2;
    bits |= bits >> 4;
    bits |= bits >> 8;
    bits |= bits >> 16;
    bits &= ~(bits >> 1);
    newlen = basestr.len + (ffs(bits)+6 >> 2);
    if (newlen > MAX_SMALL_STRING) {
	base = ALLOC_LSTRING(newlen);
	if (!base.p)
	    return base;
	dest = SV_LSTRING(base);
	SV_LSTRREF(base) = 0;
	SV_LSTRLEN(base) = newlen;
    } else {
	base = ALLOC_STRING(newlen);
	if (!base.p)
	    return base;
	dest = SV_STRING(base);
	SV_STRREF(base) = 0;
	SV_STRLEN(base) = newlen;
    }
    amemcpy(dest, basestr.start, basestr.len);
    dest += newlen -1;
    clone.i = (p_uint)clone.i >> 1;
    do {
	int d;

	d = (clone.i & 0xf) + '0';
	if (d > '9')
	    d += 'a' - '0' - 10;
	*dest-- = d;
    } while (clone.i >>= 4);
    *dest = '#';
    return make_string_global(base);
}

svalue *clone_object(svalue *sp, struct frame *fp) {
    svalue ob, new, *variables, uid;
    p_int size;

    ob = *sp;
    inter_sp = sp;
    inter_fp = fp;
    if (SV_TYPE(ob) != T_OBJECT) {
	if (!SV_IS_STRING(ob)) {
	}
	ob = find_object(ob, MAX_INHERIT_DEPTH);
    }
    new = ALLOC_OBJECT();
    if (!new.p)
	goto nomem;
    SV_OBJECT(new).program = SV_OBJECT(ob).program;
    size = SV_OBJECT(ob).program->global_variables * sizeof(svalue);
    SV_OBJECT(new).variable = variables = alloc_gen(size);
    if (!variables)
	goto nomem2;
    bzero(variables, size);
    if (SV_OBJECT(ob).dirname.i & 1 | SV_OBJECT(ob).basename.i) {
	union svalue basename, dirname;

	if (SV_OBJECT(ob).dirname.i & 1) {
	    dirname = SV_OBJECT(ob).dirname;
	    basename = REF_INC(SV_OBJECT(ob).basename);
	} else {
	    struct program *prog;

	    prog = (struct program *)SV_OBJECT(ob).dirname.p;
	    dirname = prog->dirname;
	    basename = clone_name(prog->basename, SV_OBJECT(ob).basename);
	    if (!basename.p) {
  nomem3:
  nomem2:
  nomem:
		;
	    }
	}
	SV_OBJECT(new).dirname = dirname;
	enter_clone_hash(new, dirname, basename);
	SV_OBJECT(new).basename = clone_name(basename, SV_OBJECT(new).basename);
	FREE_ALLOCED_SVALUE(basename);
	if (!SV_OBJECT(new).basename.p)
	    goto nomem3;
	REF_INC(SV_OBJECT(new).dirname);
    } else {
	struct program *prog;

	SV_OBJECT(new).dirname = SV_OBJECT(ob).dirname;
	prog = (struct program *)SV_OBJECT(ob).dirname.p;
	enter_clone_hash(new, prog->dirname, prog->basename);
    }
    SV_OBJECT(new).x.uid = &nil_uid;
    push_svalue(ob);
    uid = call_hook(driver_hook[H_CLONE_UID], new, 1);
    if (SV_IS_NUMBER(uid) ? uid.i : !SV_IS_STRING(uid)) {
	error(IE_HOOKFAIL, H_LOAD_UID<<1);
    } else {
	SV_OBJECT(new).x.uid = add_uid(uid);
    }
    FREE_SVALUE(uid);
    ob = *sp;
    FREE_ALLOCED_SVALUE(ob);
    if (inter_errno) {
	/* FIXME: destruct new */
    }
    *sp = new;
    return sp;
}

void remove_object_hash(struct object *ob) {
    char ***search, **curr, **prev;
    search = &ob->next.hash;
    curr = *search;
    do {
	prev = curr;
	curr = *(char ***)curr;
    } while ((char ***)curr != search);
    *prev = *curr;
}

static union svalue load_object(union svalue,
char *, mp_int, char *, mp_int, mp_int, p_int, char ***, int);

/* we require objects to be named without the file name extension */

union svalue find_object(union svalue name, int load) {
    uint8 *dirstart;
    uint8 *basestart, *end, c;
    mp_int namelen, baselen, dirlen;
    p_int clone;
    mp_uint hash;
    char ***anchor, **curr, **prev, save;

    dirstart = sv_string(name, &namelen);
    while (*dirstart == '/' && namelen) {
	dirstart++;
	namelen--;
    }
    clone = split_basename(dirstart, &namelen).i;
    save = dirstart[-1];
    dirstart[-1] = '/';
    end = &dirstart[namelen];
    do {
	c = *--end;
    } while (c != '/');
    dirstart[-1] = save;
    basestart = &end[1];
    dirlen = end - dirstart;
    baselen = namelen - dirlen + 1;
    if (dirlen + 1 == 0)
	dirlen = 0;
    hash = uhash(dirstart, dirlen);
    hash ^= uhash(basestart, baselen);
    hash ^= clone;
    hash ^= hash / (OTABLE_SIZE*OTABLE_SIZE);
    hash ^= hash / OTABLE_SIZE;
    hash &= OTABLE_SIZE - 1;
    anchor = &otable[hash];
    curr = *anchor;
    prev = 0;
    while (curr != (char **)anchor) {
	union svalue curr_dirname;
	uint8 *curr_dirstart, *curr_basestart;
	mp_uint curr_baselen;

	curr_dirname = NXT_TO_OB(curr)->dirname;
	if (curr_dirname.i & 1) {
	    curr_basestart =
	      sv_string(NXT_TO_OB(curr)->basename, &curr_baselen);
	    if (clone != split_basename(curr_basestart, &curr_baselen).i)
		goto no_match;
	} else {
	    struct program *prog;

	    if (clone != NXT_TO_OB(curr)->basename.i)
		goto no_match;
	    prog = (struct program *)curr_dirname.p;
	    curr_basestart = sv_string(prog->basename, &curr_baselen);
	    curr_dirname = prog->dirname;
	}
	if (curr_baselen != baselen)
	    goto no_match;
	if (SV_STR_IS_LONG(curr_dirname)) {
	    if (dirlen != SV_LSTRLEN(curr_dirname))
		goto no_match;
	    curr_dirstart = SV_LSTRING(curr_dirname);
	} else {
	    if (dirlen != SV_STRLEN(curr_dirname))
		goto no_match;
	    curr_dirstart = SV_STRING(curr_dirname);
	}
	if (!memcmp(curr_dirstart, dirstart, dirlen) &&
	    !memcmp(curr_basestart, basestart, baselen) )
	{
	    if (prev) {
		*prev = *curr;
		*curr = *(char **)anchor;
		*anchor = curr;
	    }
	    return TO_SVALUE(NXT_TO_OB(curr));
	}
      no_match:
	prev = curr;
	curr = *(char ***)curr;
    }
    if (!load)
	return SV_NULL;
    return
      load_object(
	name, dirstart, dirlen, basestart, baselen, namelen,
	clone, anchor, load);
}

/*
 * load_object() exists to avoid registers to be taken away from the critical
 * path in find_object() . It would not be necessary if C allowed to declare
 * critical paths and compilers had better register allocation shemes.
 */

static union svalue load_object(
    union svalue name,
    char *dirstart, mp_int dirlen, char *basestart, mp_int baselen,
    mp_int namelen, p_int clone, char ***anchor, int depth)
{
    struct object *ob;
    union svalue uid, sv;
    int retries;
    struct program *new_prog;

    sv = ALLOC_OBJECT();
    if (!sv.p) {
	/* alloc() has already called error() */
	return sv;
    }
    ob = &SV_OBJECT(sv);
    ob->program = &nil_program;
    ob->dirname = make_global_string(dirstart, dirlen);
    ob->basename = make_global_string(basestart, baselen);
    ob->x.uid = &nil_uid;
    push_svalue(name);
    uid = call_hook(driver_hook[H_LOAD_UID], sv, 1);
    if (SV_IS_NUMBER(uid) ? uid.i : !SV_IS_STRING(uid)) {
	error(IE_HOOKFAIL, H_LOAD_UID<<1);
    } else {
	ob->x.uid = add_uid(uid);
    }
    FREE_SVALUE(uid);
    if (inter_errno) {
	if (ob->dirname.p)
  raise_error:
	    _free_svalue(ob->dirname);
	if (ob->basename.p)
	    _free_svalue(ob->basename);
	_free_svalue(TO_SVALUE(ob));
	return SV_NULLP;
    }
    if (clone) {
	union svalue basename;

	ob->basename = clone_name(basename = ob->basename, (union svalue)clone);
	FREE_ALLOCED_SVALUE(basename);
	if (!ob->basename.p)
	    goto raise_error;
    }

    /* enter ob in hashtable */
    ob->next.hash = *anchor;
    *anchor = (char **)&ob->next.hash;

    retries = MAX_INHERIT_DEPTH;
    inter_fp[1].previous = inter_fp;
    inter_fp++;
    do {
	inter_fp->object = TO_SVALUE(ob);
	if (new_prog = compile_file(dirstart, namelen, 0)) {
	    ob->program = new_prog;
	    new_prog->dirname = ob->dirname;
	    new_prog->basename = ob->basename;
	    if (clone) {
		REF_INC(ob->dirname );
		REF_INC(ob->basename);
	    } else {
		ob->dirname.p = (char *)new_prog;
		ob->basename.i = 0;
	    }
	    return TO_SVALUE(ob);
	}
	if (!inherit_file.p) {
	    goto raise_error;
	}
	if (depth-1 == 0) {
	    error(IE_INHERIT_DEPTH);
	    goto raise_error;
	}
	find_object(inherit_file, depth - 1);
    } while (--retries);
    inter_fp--;
    error(IE_INHERIT_DEPTH);
    goto raise_error;
}

void _free_object(union svalue ob) {
    FREE_SVALUE(SV_OBJECT(ob).dirname);
    FREE_SVALUE(SV_OBJECT(ob).basename);
    free_block(ob.p, sizeof(struct object));
}

/* async_current_time == (uint32)current_time / ASYNC_GRANULARITY */

p_int async_load;
int time_to_swap, time_to_swap_variables;

void object_async() {
    int ref_time = async_current_time;
    do {
	static int i;

	char *anchor, *curr;

	i = i+sizeof otable[0] & (OTABLE_SIZE - 1)*sizeof(otable[0]);
	curr = anchor = (char *)&otable + i;
	while ((curr = *(char **)curr) != anchor) {
	    int time_since_ref;
	    time_since_ref = (uint16)(ref_time - NXT_TO_OB(curr)->last_touched);

	    if (NXT_TO_OB(curr)->flags & (O_RESET_NONE|O_RESET_CUSTOM) ?
		 (NXT_TO_OB(curr)->flags & O_RESET_CUSTOM &&
		   (int16)(ref_time - NXT_TO_OB(curr)->reset.next) >= 0 ) :
		 (uint16)( ref_time - NXT_TO_OB(curr)->reset.last) >=
		   TIME_TO_RESET/ASYNC_GRANULARITY &&
		 NXT_TO_OB(curr)->last_touched ==
		   (uint16)(NXT_TO_OB(curr)->reset.last + 1)	)
	    {
		union svalue nxt;

		PUSH_NUMBER(0);
		nxt = call_hook(
		    driver_hook[H_RESET], TO_SVALUE(NXT_TO_OB(curr)), 1);
		if (!SV_IS_NUMBER(nxt)) {
		    /* includes CONST_INVALID for call failed */

		    FREE_SVALUE(nxt);
		    NXT_TO_OB(curr)->flags |= O_RESET_NONE;
		} else if (nxt.i) {
		    NXT_TO_OB(curr)->flags |= O_RESET_CUSTOM;
		    NXT_TO_OB(curr)->reset.next =
			ref_time + (p_uint)(nxt.i >> 1)/ASYNC_GRANULARITY;
		} else {
		    NXT_TO_OB(curr)->flags &= ~O_RESET_CUSTOM;
		    NXT_TO_OB(curr)->reset.last = ref_time;
		    NXT_TO_OB(curr)->last_touched = ref_time - 1;
		}
	    }
#if TIME_TO_CLEAN_UP > 0
	    else if (time_since_ref > TIME_TO_CLEAN_UP/ASYNC_GRANULARITY &&
		     NXT_TO_OB(curr)->flags & O_WILL_CLEAN_UP)
	    {
		union svalue sv;
		int was_swapped = NXT_TO_OB(curr)->flags & O_SWAPPED ;
		int save_touched = NXT_TO_OB(curr)->last_touched;

		push_svalue(TO_SVALUE(NXT_TO_OB(curr)));
		PUSH_NUMBER( NXT_TO_OB(curr)->flags & O_CLONE ? 0 :
		  ( O_PROG_SWAPPED(NXT_TO_OB(curr)) ? 1 :
		    NXT_TO_OB(curr)->program->ref)
		);
		sv = call_hook(
		    driver_hook[H_CLEAN_UP], TO_SVALUE(NXT_TO_OB(curr)), 2);
		NXT_TO_OB(curr)->last_touched = save_touched;
		if (!sv.i && was_swapped) {
		     NXT_TO_OB(curr)->flags &= ~O_WILL_CLEAN_UP;
		}
		FREE_SVALUE(sv);
	    }
#endif /* TIME_TO_CLEAN_UP */
	    async_load--;
	}
    } while (async_load > 0);
}

void remove_destructed_objects() {
    union svalue ob, next;

    for (ob = obj_list_destructed; ob.i; ob = next) {
	next = SV_OBJECT(ob).next.destructed;
	FREE_ALLOCED_SVALUE(ob);
    }
    CLEAR_JOB(remove_destructed_objects);
    EXTRA_JOBS();
}

struct object_x *alloc_object_x(svalue ob) {
    union object_xu x = SV_OBJECT(ob).x;
    if (!OX_VALID(x.x)) {
	svalue sv = ALLOC_TTS(T_INTERNAL, IT_X_OBJ, 1,
	  sizeof (p_int) + sizeof(struct object_x));
	if (sv.p) {
	    ((struct object_x *)&sv.p[sizeof(p_int) - 1])->uid = x.uid;
	    x.x = (struct object_x *)&sv.p[sizeof(p_int) - 1];
	    SV_OBJECT(ob).x = x;
	    x.x->user = 0;
	    x.x->shadowing = 0;
	    x.x->shadowed_by = 0;
	}
    }
    return x.x;
}

int validate_shadowing(struct frame *fp, svalue ob) {
    return SV_OBJECT(fp->object).x.uid->self->name.p
	== driver_hook[H_PRIVILEGED_UID].p;
}

svalue *f_shadow(svalue *sp, struct frame *fp) {
    struct object_x *victim_x, *shadow_x;
    svalue ob = *sp;
    if (OP_X_FLAGS(SV_OBJECTP(ob)) & O_X_SHADOWED) {
        do {
            ob = SV_OBJECT(ob).x.x->shadowed_by;
        } while (SV_OBJECT(ob).x.x->shadowed_by.i);
    }
    switch(0) { default:
	if (validate_shadowing (fp, ob)) {
	    victim_x = alloc_object_x(ob);
	    shadow_x = alloc_object_x(fp->object);
	    if (victim_x && shadow_x) {
		victim_x->shadowed_by = fp->object;
		OX_FLAGS(victim_x) |= O_X_SHADOWED;
		shadow_x->shadowing = ob;
		break;
	    }
	}
	FREE_ALLOCED_SVALUE(ob);
	sp->i = 0;
    }
    return sp;
}