#include	"cem.h"
#define	STD_OBJ	1
#include	"stdobj.h"
#include	"symbol.h"
#include	"type.h"

/*
 *	Type table routines.
 */

extern char	*strchr();

/*
 *	Comparison routines.  "Identical type" equivalence.
 *	We believe file modtimes unless we are told to doubt.
 */

static int
cmp_enum(p, q)
type	*p;
type	*q;
{
	register e_data	*e;
	register e_data	*f;
	register e_elt	*g;
	register e_elt	*h;
	register int	i;

	e = p->d.e;
	f = q->d.e;

	if (modtimes && e->e_line == f->e_line && match_times(e->e_file, f->e_file))
		return 0;

	if (e->e_name != f->e_name)
	{
		if (e->e_name == NULL)
			return -f->e_name->sy_index;
		else if (f->e_name == NULL)
			return e->e_name->sy_index;
		else
			return e->e_name->sy_index - f->e_name->sy_index;
	}

	if ((i = e->e_low - f->e_low) != 0 || (i = e->e_high - f->e_high) != 0)
		return i;

	g = e->e_list;
	h = f->e_list;

	while (g != NULL)
	{
		if (h == NULL)
			return -1;

		if (g->e_name != h->e_name)
			return g->e_name->sy_index - h->e_name->sy_index;

		if ((i = g->e_value - h->e_value) != 0)
			return i;

		g = g->e_next;
		h = h->e_next;
	}

	if (h != NULL)
		return 1;

	return 0;
}

static int
cmp_struct(p, q)
type	*p;
type	*q;
{
	register s_data	*e;
	register s_data	*f;
	register s_elt	*g;
	register s_elt	*h;
	register int	i;

	e = p->d.s;
	f = q->d.s;

	if (modtimes && e->s_line == f->s_line && match_times(e->s_file, f->s_file))
		return 0;

	if (e->s_name != f->s_name)
	{
		if (e->s_name == NULL)
			return -f->s_name->sy_index;
		else if (f->s_name == NULL)
			return e->s_name->sy_index;
		else
			return e->s_name->sy_index - f->s_name->sy_index;
	}

	if ((i = e->s_size - f->s_size) != 0)
		return i;

	g = e->s_list;
	h = f->s_list;

	while (g != NULL)
	{
		if (h == NULL)
			return -1;

		if (g->s_name != h->s_name)
			return g->s_name->sy_index - h->s_name->sy_index;

		if (g->s_type != h->s_type)
			return g->s_type->t_index - h->s_type->t_index;

		g = g->s_next;
		h = h->s_next;
	}

	if (h != NULL)
		return 1;

	return 0;
}

static int
cmp_union(p, q)
type	*p;
type	*q;
{
	register u_data	*e;
	register u_data	*f;
	register u_elt	*g;
	register u_elt	*h;
	register int	i;

	e = p->d.u;
	f = q->d.u;

	if (modtimes && e->u_line == f->u_line && match_times(e->u_file, f->u_file))
		return 0;

	if (e->u_name != f->u_name)
	{
		if (e->u_name == NULL)
			return -f->u_name->sy_index;
		else if (f->u_name == NULL)
			return e->u_name->sy_index;
		else
			return e->u_name->sy_index - f->u_name->sy_index;
	}

	if ((i = e->u_size - f->u_size) != 0)
		return i;

	g = e->u_list;
	h = f->u_list;

	while (g != NULL)
	{
		if (h == NULL)
			return -1;

		if (g->u_name != h->u_name)
			return g->u_name->sy_index - h->u_name->sy_index;

		if (g->u_type != h->u_type)
			return g->u_type->t_index - h->u_type->t_index;

		g = g->u_next;
		h = h->u_next;
	}

	if (h != NULL)
		return 1;

	return 0;
}

/*
 *	We win a lot by trusting the file/modtime linenumber
 *	of a struct/unions declaration.
 */
#define	THASHSZ	37
#define	TPRIME0	7
#define	TPRIME1	31

/*
 *	Type hashing by filename/modtime and line number.
 */
static type	*
type_hash(h0, h1, t)
long	h0;
long	h1;
type	*t;
{
	typedef struct thash	thash;

	struct thash
	{
		long	h0;
		long	h1;
		type	*t;
		thash	*next;
	};

	register long	i;
	register thash	*p;
	static thash	*thasht[THASHSZ];

	i = TPRIME0 * h0 + TPRIME1 * h1;

	if (i < 0)
		i = -i;

	i %= THASHSZ;

	for (p = thasht[i]; p != NULL; p = p->next)
	{
		if (p->h0 == h0 && p->h1 == h1)
			return p->t;

	}

	if (t == NULL)
		return NULL;

	p = talloc(thash);
	p->h0 = h0;
	p->h1 = h1;
	p->t = t;
	p->next = thasht[i];
	thasht[i] = p;
	return t;
}

/*
 *	Lookup a type hashing by filename/modtime and line number.
 */
type	*
lookup_hash(s, l)
symbol	*s;
long	l;
{
	if (modtimes && strchr(s->sy_name, TIME_SEP) != NULL)
		return type_hash(s->sy_index, l, (type *)NULL);
	else
		return NULL;
}

/*
 *	Enter a type hashing by filename/modtime and line number.
 */
void
enter_hash(s, l, t)
symbol	*s;
long	l;
type	*t;
{
	if (modtimes && strchr(s->sy_name, TIME_SEP) != NULL)
		type_hash(s->sy_index, l, t);
}

/*
 *	Type table manager.  Pass it a type and it will return you the
 *	(strongly) equivalent type that characterises it.  i.e. it
 *	'interns' a type.  Type comparison is then pointer comparison.
 */
type	*
find_type(t)
register type	*t;
{
	typedef struct ttnode	ttnode;

	struct ttnode
	{
		type	*tt_type;
		ttnode	*tt_left;
		ttnode	*tt_right;
	};

	register type	*p;
	register type	*q;
	register int	i;
	register ttnode	**n;
	register ttnode	*tt;

	static ttnode	*type_table[(int)t_types];

	n = &type_table[(int)t->t_type];

	while (*n != NULL)
	{
		p = t;
		q = (*n)->tt_type;

		if ((i = ((int)p->t_type) - ((int)q->t_type)) == 0)
		{
			switch (p->t_type)
			{
			case t_arrayof:
				if ((i = p->d.dim - q->d.dim) == 0)
					i = p->t_subtype->t_index - q->t_subtype->t_index;

				break;

			case t_basetype:
				i = p->d.mask - q->d.mask;
				break;

			case t_bitfield:
				if ((i = p->d.size - q->d.size) == 0)
					i = p->t_subtype->t_index - q->t_subtype->t_index;

				break;

			case t_dimless:
			case t_ftnreturning:
			case t_ptrto:
				i = p->t_subtype->t_index - q->t_subtype->t_index;
				break;

			case t_enum:
				i = cmp_enum(p, q);
				break;

			case t_structof:
				i = cmp_struct(p, q);
				break;

			case t_unionof:
				i = cmp_union(p, q);
				break;

			default:
				fprint(2, "%s: find_type - bad type\n", my_name);
				exit(1);
			}

			if (i == 0)
			{
				delete_type(t);
				return (*n)->tt_type;
			}
		}

		n = i < 0 ? &((*n)->tt_left) : &((*n)->tt_right);
	}

	switch (t->t_type)
	{
	case t_structof:
		enter_hash(t->d.s->s_file, t->d.s->s_line, t);
		break;

	case t_unionof:
		enter_hash(t->d.u->u_file, t->d.u->u_line, t);
	}

	tt = talloc(ttnode);
	*n = tt;
	tt->tt_type = t;
	tt->tt_left = NULL;
	tt->tt_right = NULL;
	t->t_index = new_type_index++;
	return t;
}
