/*
 * _sio_exttable.c
 *
 * Externally represented objects
 *
 * Authors: Khalil Amiri, CMU SCS/ECE, July 18 1997
 *          Sean Levy, CMU SCS, July 1999
 */
/*
 * Copyright (c) of Carnegie Mellon University, 1996,1997,1998,1999.
 *
 * Permission to reproduce, use, and prepare derivative works of
 * this software for internal use is granted provided the copyright
 * and "No Warranty" statements are included with all reproductions
 * and derivative works. This software may also be redistributed
 * without charge provided that the copyright and "No Warranty"
 * statements are included in all redistributions.
 *
 * NO WARRANTY. THIS SOFTWARE IS FURNISHED ON AN "AS IS" BASIS.
 * CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER
 * EXPRESSED OR IMPLIED AS TO THE MATTER INCLUDING, BUT NOT LIMITED
 * TO: WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY
 * OF RESULTS OR RESULTS OBTAINED FROM USE OF THIS SOFTWARE. CARNEGIE
 * MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT
 * TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
 */

#include "_sio_internal.h"

#define	_SIO_EXTREP_VALMASK	(INT_MAX >> _SIO_EXTREP_USIZE)
#define	_SIO_EXTREP_VAL(ext)	((ext) & _SIO_EXTREP_VALMASK)
#define	_SIO_EXTREP_CREATE(u,v)	(((u) << _SIO_EXTREP_USHIFT) | (v))

struct _sio_exttable {
  NASD_DECLARE_MUTEX(mutex)
	TAILQ_HEAD(, _sio_exttable_entry) queue;
	unsigned int uniq, next;
};

struct _sio_exttable_entry {
	TAILQ_ENTRY(_sio_exttable_entry) link;
	int external;
	void *internal;
};

static struct _sio_exttable_entry *
    _sio_exttable_find_internal(struct _sio_exttable *, void *internal);
static struct _sio_exttable_entry *
    _sio_exttable_find_external(struct _sio_exttable *, int external);

void *
_sio_exttable_create(unsigned int tableuniq, unsigned int expected)
{
	struct _sio_exttable *net;
	int rc;

	net = malloc(sizeof *net);
	if (net == NULL)
		return (NULL);

	rc = nasd_mutex_init(&net->mutex);
	if (rc) {
	  free(net);
	  return NULL;
	}
	TAILQ_INIT(&net->queue);
	net->uniq = tableuniq;
	net->next = 0;

	/* XXX expected would be used if hash table, etc. */

	return (net);
}

static struct _sio_exttable_entry *
_sio_exttable_find_internal(struct _sio_exttable *tp, void *internal)
{
	struct _sio_exttable_entry *ep;

	for (ep = tp->queue.tqh_first; ep != NULL; ep = ep->link.tqe_next)
		if (ep->internal == internal)
			return (ep);
	return NULL;
}

static struct _sio_exttable_entry *
_sio_exttable_find_external(struct _sio_exttable *tp, int external)
{
	struct _sio_exttable_entry *ep;

	for (ep = tp->queue.tqh_first; ep != NULL; ep = ep->link.tqe_next)
		if (ep->external == external)
			return (ep);
	return NULL;
}

void
_sio_exttable_register(void *tpv, void *internal)
{
	struct _sio_exttable *tp = tpv;
	struct _sio_exttable_entry *ep;
	int external;

	if (internal == NULL) {
		_sio_panic("_sio_exttable_register: NULL");
		/* NOTREACHED */
	}

	ep = malloc(sizeof *ep);
	if (ep == NULL)
		_sio_panic("_sio_exttable_register: out of memory");

	ep->internal = internal;

	NASD_LOCK_MUTEX(tp->mutex);

	do {
		/* try the next external representation */
		external = _SIO_EXTREP_CREATE(tp->uniq, tp->next);

		/* increment the value */
		tp->next++;
		tp->next &= _SIO_EXTREP_VALMASK;
	} while (_sio_exttable_find_external(tp, external));

	ep->external = external;

	_sio_rcobj_ref(internal);
	TAILQ_INSERT_TAIL(&tp->queue, ep, link);

  NASD_UNLOCK_MUTEX(tp->mutex);
}

void
_sio_exttable_unregister(void *tpv, void *internal)
{
	struct _sio_exttable *tp = tpv;
	struct _sio_exttable_entry *ep;

	if (internal == NULL) {
		_sio_panic("_sio_exttable_destroy: NULL");
		/* NOTREACHED */
	}

  NASD_LOCK_MUTEX(tp->mutex);

	ep = _sio_exttable_find_internal(tp, internal);
	if (ep == NULL)
		_sio_panic("_sio_exttable_unregister: %p not in table %p",
		    internal, tp);
	TAILQ_REMOVE(&tp->queue, ep, link);
	_sio_rcobj_unref(internal);

  NASD_UNLOCK_MUTEX(tp->mutex);

	free(ep);
}

int
_sio_exttable_externalize(void *tpv, void *internal)
{
	struct _sio_exttable *tp = tpv;
	struct _sio_exttable_entry *ep;
	int rv;

	if (internal == NULL) {
		_sio_panic("_sio_exttable_externalize: NULL");
		/* NOTREACHED */
	}

  NASD_LOCK_MUTEX(tp->mutex);

	ep = _sio_exttable_find_internal(tp, internal);
	if (ep == NULL)
		_sio_panic("_sio_exttable_externalize: %p not in table %p",
		    internal, tp);

	rv = ep->external;
	_sio_rcobj_unref(internal);

  NASD_UNLOCK_MUTEX(tp->mutex);
	return (rv);
}

void *
_sio_exttable_internalize(void *tpv, int external)
{
	struct _sio_exttable *tp = tpv;
	struct _sio_exttable_entry *ep;
	void *rv;

	rv = NULL;
  NASD_LOCK_MUTEX(tp->mutex);

	ep = _sio_exttable_find_external(tp, external);
	if (ep != NULL) {
		_sio_rcobj_ref(ep->internal);
		rv = ep->internal;
	} else {
		rv = NULL;
	}

  NASD_UNLOCK_MUTEX(tp->mutex);

	return (rv);
}

/* Local Variables:  */
/* indent-tabs-mode: nil */
/* tab-width: 2 */
/* End: */
