// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package runtime
#include "runtime.h"
#include "arch_GOARCH.h"
#include "type.h"
#include "typekind.h"
#include "malloc.h"
#include "../../cmd/ld/textflag.h"

func printiface(i Iface) {
	runtime·printf("(%p,%p)", i.tab, i.data);
}

func printeface(e Eface) {
	runtime·printf("(%p,%p)", e.type, e.data);
}

static	Itab*	hash[1009];
static	Lock	ifacelock;

static Itab*
itab(InterfaceType *inter, Type *type, int32 canfail)
{
	int32 locked;
	int32 ni;
	Method *t, *et;
	IMethod *i, *ei;
	uint32 h;
	String *iname, *ipkgPath;
	Itab *m;
	UncommonType *x;
	Type *itype;
	Eface err;

	if(inter->mhdr.len == 0)
		runtime·throw("internal error - misuse of itab");

	locked = 0;

	// easy case
	x = type->x;
	if(x == nil) {
		if(canfail)
			return nil;
		iname = inter->m[0].name;
		goto throw;
	}

	// compiler has provided some good hash codes for us.
	h = inter->hash;
	h += 17 * type->hash;
	// TODO(rsc): h += 23 * x->mhash ?
	h %= nelem(hash);

	// look twice - once without lock, once with.
	// common case will be no lock contention.
	for(locked=0; locked<2; locked++) {
		if(locked)
			runtime·lock(&ifacelock);
		for(m=runtime·atomicloadp(&hash[h]); m!=nil; m=m->link) {
			if(m->inter == inter && m->type == type) {
				if(m->bad) {
					m = nil;
					if(!canfail) {
						// this can only happen if the conversion
						// was already done once using the , ok form
						// and we have a cached negative result.
						// the cached result doesn't record which
						// interface function was missing, so jump
						// down to the interface check, which will
						// do more work but give a better error.
						goto search;
					}
				}
				if(locked)
					runtime·unlock(&ifacelock);
				return m;
			}
		}
	}

	ni = inter->mhdr.len;
	m = runtime·persistentalloc(sizeof(*m) + ni*sizeof m->fun[0], 0, &mstats.other_sys);
	m->inter = inter;
	m->type = type;

search:
	// both inter and type have method sorted by name,
	// and interface names are unique,
	// so can iterate over both in lock step;
	// the loop is O(ni+nt) not O(ni*nt).
	i = inter->m;
	ei = i + inter->mhdr.len;
	t = x->m;
	et = t + x->mhdr.len;
	for(; i < ei; i++) {
		itype = i->type;
		iname = i->name;
		ipkgPath = i->pkgPath;
		for(;; t++) {
			if(t >= et) {
				if(!canfail) {
				throw:
					// didn't find method
					runtime·newTypeAssertionError(
						nil, type->string, inter->string,
						iname, &err);
					if(locked)
						runtime·unlock(&ifacelock);
					runtime·panic(err);
					return nil;	// not reached
				}
				m->bad = 1;
				goto out;
			}
			if(t->mtyp == itype && t->name == iname && t->pkgPath == ipkgPath)
				break;
		}
		if(m)
			m->fun[i - inter->m] = t->ifn;
	}

out:
	if(!locked)
		runtime·panicstring("invalid itab locking");
	m->link = hash[h];
	runtime·atomicstorep(&hash[h], m);
	runtime·unlock(&ifacelock);
	if(m->bad)
		return nil;
	return m;
}

// call the callback for every itab that is currently allocated.
void
runtime·iterate_itabs(void (*callback)(Itab*))
{
	int32 i;
	Itab *tab;

	for(i = 0; i < nelem(hash); i++) {
		for(tab = hash[i]; tab != nil; tab = tab->link) {
			callback(tab);
		}
	}
}

static void
copyin(Type *t, void *src, void **dst)
{
	uintptr size;
	void *p;
	Alg *alg;

	size = t->size;
	alg = t->alg;

	if(size <= sizeof(*dst))
		alg->copy(size, dst, src);
	else {
		p = runtime·cnew(t);
		alg->copy(size, p, src);
		*dst = p;
	}
}

static void
copyout(Type *t, void **src, void *dst)
{
	uintptr size;
	Alg *alg;

	size = t->size;
	alg = t->alg;

	if(size <= sizeof(*src))
		alg->copy(size, dst, src);
	else
		alg->copy(size, dst, *src);
}

#pragma textflag NOSPLIT
func typ2Itab(t *Type, inter *InterfaceType, cache **Itab) (tab *Itab) {
	tab = itab(inter, t, 0);
	runtime·atomicstorep(cache, tab);
}

#pragma textflag NOSPLIT
func convT2I(t *Type, inter *InterfaceType, cache **Itab, elem *byte) (ret Iface) {
	Itab *tab;

	tab = runtime·atomicloadp(cache);
	if(!tab) {
		tab = itab(inter, t, 0);
		runtime·atomicstorep(cache, tab);
	}
	ret.tab = tab;
	copyin(t, elem, &ret.data);
}

#pragma textflag NOSPLIT
func convT2E(t *Type, elem *byte) (ret Eface) {
	ret.type = t;
	copyin(t, elem, &ret.data);
}

static void assertI2Tret(Type *t, Iface i, byte *ret);

#pragma textflag NOSPLIT
func assertI2T(t *Type, i Iface) (ret byte, ...) {
	assertI2Tret(t, i, &ret);
}

static void
assertI2Tret(Type *t, Iface i, byte *ret)
{
	Itab *tab;
	Eface err;

	tab = i.tab;
	if(tab == nil) {
		runtime·newTypeAssertionError(
			nil, nil, t->string,
			nil, &err);
		runtime·panic(err);
	}
	if(tab->type != t) {
		runtime·newTypeAssertionError(
			tab->inter->string, tab->type->string, t->string,
			nil, &err);
		runtime·panic(err);
	}
	copyout(t, &i.data, ret);
}

#pragma textflag NOSPLIT
func assertI2T2(t *Type, i Iface) (ret byte, ...) {
	bool *ok;
	int32 wid;

	wid = t->size;
	ok = (bool*)(&ret + wid);

	if(i.tab == nil || i.tab->type != t) {
		*ok = false;
		runtime·memclr(&ret, wid);
		return;
	}

	*ok = true;
	copyout(t, &i.data, &ret);
}

func assertI2TOK(t *Type, i Iface) (ok bool) {
	ok = i.tab!=nil && i.tab->type==t;
}

static void assertE2Tret(Type *t, Eface e, byte *ret);

#pragma textflag NOSPLIT
func assertE2T(t *Type, e Eface) (ret byte, ...) {
	assertE2Tret(t, e, &ret);
}

static void
assertE2Tret(Type *t, Eface e, byte *ret)
{
	Eface err;

	if(e.type == nil) {
		runtime·newTypeAssertionError(
			nil, nil, t->string,
			nil, &err);
		runtime·panic(err);
	}
	if(e.type != t) {
		runtime·newTypeAssertionError(
			nil, e.type->string, t->string,
			nil, &err);
		runtime·panic(err);
	}
	copyout(t, &e.data, ret);
}

#pragma textflag NOSPLIT
func assertE2T2(t *Type, e Eface) (ret byte, ...) {
	bool *ok;
	int32 wid;

	wid = t->size;
	ok = (bool*)(&ret + wid);

	if(t != e.type) {
		*ok = false;
		runtime·memclr(&ret, wid);
		return;
	}

	*ok = true;
	copyout(t, &e.data, &ret);
}

func assertE2TOK(t *Type, e Eface) (ok bool) {
	ok = t==e.type;
}

func convI2E(i Iface) (ret Eface) {
	Itab *tab;

	ret.data = i.data;
	if((tab = i.tab) == nil)
		ret.type = nil;
	else
		ret.type = tab->type;
}

func assertI2E(inter *InterfaceType, i Iface) (ret Eface) {
	Itab *tab;
	Eface err;

	tab = i.tab;
	if(tab == nil) {
		// explicit conversions require non-nil interface value.
		runtime·newTypeAssertionError(
			nil, nil, inter->string,
			nil, &err);
		runtime·panic(err);
	}
	ret.data = i.data;
	ret.type = tab->type;
}

func assertI2E2(inter *InterfaceType, i Iface) (ret Eface, ok bool) {
	Itab *tab;

	USED(inter);
	tab = i.tab;
	if(tab == nil) {
		ret.type = nil;
		ok = 0;
	} else {
		ret.type = tab->type;
		ok = 1;
	}
	ret.data = i.data;
}

func convI2I(inter *InterfaceType, i Iface) (ret Iface) {
	Itab *tab;

	ret.data = i.data;
	if((tab = i.tab) == nil)
		ret.tab = nil;
	else if(tab->inter == inter)
		ret.tab = tab;
	else
		ret.tab = itab(inter, tab->type, 0);
}

void
runtime·ifaceI2I(InterfaceType *inter, Iface i, Iface *ret)
{
	Itab *tab;
	Eface err;

	tab = i.tab;
	if(tab == nil) {
		// explicit conversions require non-nil interface value.
		runtime·newTypeAssertionError(
			nil, nil, inter->string,
			nil, &err);
		runtime·panic(err);
	}
	ret->data = i.data;
	ret->tab = itab(inter, tab->type, 0);
}

func assertI2I(inter *InterfaceType, i Iface) (ret Iface) {
	runtime·ifaceI2I(inter, i, &ret);
}

func assertI2I2(inter *InterfaceType, i Iface) (ret Iface, ok bool) {
	Itab *tab;

	tab = i.tab;
	if(tab != nil && (tab->inter == inter || (tab = itab(inter, tab->type, 1)) != nil)) {
		ret.data = i.data;
		ret.tab = tab;
		ok = 1;
	} else {
		ret.data = 0;
		ret.tab = 0;
		ok = 0;
	}
}

void
runtime·ifaceE2I(InterfaceType *inter, Eface e, Iface *ret)
{
	Type *t;
	Eface err;

	t = e.type;
	if(t == nil) {
		// explicit conversions require non-nil interface value.
		runtime·newTypeAssertionError(
			nil, nil, inter->string,
			nil, &err);
		runtime·panic(err);
	}
	ret->data = e.data;
	ret->tab = itab(inter, t, 0);
}

bool
runtime·ifaceE2I2(InterfaceType *inter, Eface e, Iface *ret)
{
	ret->tab = itab(inter, e.type, 1);
	if(ret->tab == nil)
		return false;
	ret->data = e.data;
	return true;
}

func reflect·ifaceE2I(inter *InterfaceType, e Eface, dst *Iface) {
	runtime·ifaceE2I(inter, e, dst);
}

func assertE2I(inter *InterfaceType, e Eface) (ret Iface) {
	runtime·ifaceE2I(inter, e, &ret);
}

func assertE2I2(inter *InterfaceType, e Eface) (ret Iface, ok bool) {
	if(e.type == nil) {
		ok = 0;
		ret.data = nil;
		ret.tab = nil;
	} else if((ret.tab = itab(inter, e.type, 1)) == nil) {
		ok = 0;
		ret.data = nil;
	} else {
		ok = 1;
		ret.data = e.data;
	}
}

func assertE2E(inter *InterfaceType, e Eface) (ret Eface) {
	Type *t;
	Eface err;

	t = e.type;
	if(t == nil) {
		// explicit conversions require non-nil interface value.
		runtime·newTypeAssertionError(
			nil, nil, inter->string,
			nil, &err);
		runtime·panic(err);
	}
	ret = e;
}

func assertE2E2(inter *InterfaceType, e Eface) (ret Eface, ok bool) {
	USED(inter);
	ret = e;
	ok = e.type != nil;
}

static uintptr
ifacehash1(void *data, Type *t, uintptr h)
{
	Alg *alg;
	uintptr size;
	Eface err;

	if(t == nil)
		return 0;

	alg = t->alg;
	size = t->size;
	if(alg->hash == runtime·nohash) {
		// calling nohash will panic too,
		// but we can print a better error.
		runtime·newErrorString(runtime·catstring(runtime·gostringnocopy((byte*)"hash of unhashable type "), *t->string), &err);
		runtime·panic(err);
	}
	if(size <= sizeof(data))
		alg->hash(&h, size, &data);
	else
		alg->hash(&h, size, data);
	return h;
}

uintptr
runtime·ifacehash(Iface a, uintptr h)
{
	if(a.tab == nil)
		return h;
	return ifacehash1(a.data, a.tab->type, h);
}

uintptr
runtime·efacehash(Eface a, uintptr h)
{
	return ifacehash1(a.data, a.type, h);
}

static bool
ifaceeq1(void *data1, void *data2, Type *t)
{
	uintptr size;
	Alg *alg;
	Eface err;
	bool eq;

	alg = t->alg;
	size = t->size;

	if(alg->equal == runtime·noequal) {
		// calling noequal will panic too,
		// but we can print a better error.
		runtime·newErrorString(runtime·catstring(runtime·gostringnocopy((byte*)"comparing uncomparable type "), *t->string), &err);
		runtime·panic(err);
	}

	eq = 0;
	if(size <= sizeof(data1))
		alg->equal(&eq, size, &data1, &data2);
	else
		alg->equal(&eq, size, data1, data2);
	return eq;
}

bool
runtime·ifaceeq_c(Iface i1, Iface i2)
{
	if(i1.tab != i2.tab)
		return false;
	if(i1.tab == nil)
		return true;
	return ifaceeq1(i1.data, i2.data, i1.tab->type);
}

bool
runtime·efaceeq_c(Eface e1, Eface e2)
{
	if(e1.type != e2.type)
		return false;
	if(e1.type == nil)
		return true;
	return ifaceeq1(e1.data, e2.data, e1.type);
}

func ifaceeq(i1 Iface, i2 Iface) (ret bool) {
	ret = runtime·ifaceeq_c(i1, i2);
}

func efaceeq(e1 Eface, e2 Eface) (ret bool) {
	ret = runtime·efaceeq_c(e1, e2);
}

func ifacethash(i1 Iface) (ret uint32) {
	Itab *tab;

	ret = 0;
	tab = i1.tab;
	if(tab != nil)
		ret = tab->type->hash;
}

func efacethash(e1 Eface) (ret uint32) {
	Type *t;

	ret = 0;
	t = e1.type;
	if(t != nil)
		ret = t->hash;
}

func reflect·unsafe_Typeof(e Eface) (ret Eface) {
	if(e.type == nil) {
		ret.type = nil;
		ret.data = nil;
	} else {
		ret = *(Eface*)(e.type);
	}
}

func reflect·unsafe_New(t *Type) (ret *byte) {
	ret = runtime·cnew(t);
}

func reflect·unsafe_NewArray(t *Type, n int) (ret *byte) {
	ret = runtime·cnewarray(t, n);
}

func reflect·typelinks() (ret Slice) {
	extern Type *typelink[], *etypelink[];
	static int32 first = 1;
	ret.array = (byte*)typelink;
	ret.len = etypelink - typelink;
	ret.cap = ret.len;
}
