/* $Id: ps_map.c,v 1.1.1.1 90/11/28 16:45:14 altenhof Exp $ */

/*
 * Copyright (C) 1990 by Digital Equipment Corporation.
 * 
 * Author: Michael P. Altenhofen, CEC Karlsruhe e-mail:
 * Altenhofen@kampus.enet.dec.com
 * 
 * This file ist part of Shared X
 * 
 * Permission to use, copy, modify, and distribute this software and its
 * documentation without fee is hereby granted, but only for non-profit  use
 * and distribution,  and provided  that the copyright notice and this notice
 * is preserved on all copies.
 * 
 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 */

#define PUBLIC extern

#define _USE_STRUCTS_

#include "glob.h"
#include "utils.h"

#undef PUBLIC

#include "defs.h"

#define _XEVENTS_
#include <X11/Xlib.h>		/* we only need XID and ScreenCount here, but
				 * for the sake of portability ... */

/* -- macros to check an resource ID -- */
#define Resource_Base( dpy )   ( dpy->resource_base )

#define SERVER_BIT 0x80000	/* 20th bit reserved */
#define CLIENT_BITS( id ) \
         ( id & 0xfff00000 )	/* hi 12 bits */
#define IDFromThisConnection( id , mask ) \
         ( ( CLIENT_BITS( id ) == mask ) && \
	    !( id & SERVER_BIT ) )

/* -- local definitions for hashing -- */
#define HASHSIZE 257

/*
 * The hash function: (server identification followed by bit 0 to bit 18
 * of the resource ID) modulo HASHSIZE
 */
#define HashID(id, whose)   (int) (((whose << 19) | \
					   (0x7ffff & id)) % HASHSIZE)

#define NullTable    ( IDMapPtr * ) ( 0 )
#define NullIDMapPtr ( IDMapPtr ) ( 0 )

typedef struct _IDMapRec {	/* one entry in our ID map */
  int whose;			/* where does this ID come from */
  int used;			/* mark unused entries here */
  XID id,			/* the id ... */
   alias;			/* ... and it's equivalence */
  struct _IDMapRec *next;	/* we use hashing with collision resolution
				 * by linking */
} IDMapRec, *IDMapPtr;

static IDMapPtr *ServerIDMaps[MAXSOCKS];	/* one hash table per server */

static IDMapPtr *FreeMaps[10];	/* list of allocated but unused hash tables */
static int free_index = -1;

static IDMapPtr FreeEntries = NullIDMapPtr;	/* list of allocated but
						 * unused hash entries */

void
_FreeIDMAP (map)
  IDMapPtr *map;
{
  if (++free_index > 9) {
    free_index = 9;
    Xfree ((char *) map);
  }
  else
    FreeMaps[free_index] = map;
}

IDMapPtr *
_AllocDMAP ()
{
  IDMapPtr *map;
  register int i;

  if (free_index < 0) {
    free_index = -1;
    map = (IDMapPtr *) Xmalloc (HASHSIZE * sizeof (IDMapPtr));
  }
  else
    map = FreeMaps[free_index--];

  for (i = 0; i < HASHSIZE; i++)
    map[i] = NullIDMapPtr;

  return (map);
}

void
FreeEntry (entry)
  IDMapPtr entry;
{
  entry->next = FreeEntries;
  FreeEntries = entry;
  entry = NullIDMapPtr;
}

IDMapPtr
_AllocEntry ()
{
  IDMapPtr newentry;

  if (FreeEntries == NullIDMapPtr)
    return ((IDMapPtr) Xmalloc (sizeof (IDMapRec)));
  else {
    newentry = FreeEntries;
    newentry->next = NullIDMapPtr;
    FreeEntries = FreeEntries->next;
    return (newentry);
  }
}

void
_InitIDMaps ()
{
  register int i;

  for (i = 0; i < MAXSOCKS; i++)
    ServerIDMaps[i] = NullTable;
}

void
_InstallIDMap (server)
  int server;
{
  register int i;

  if ((ServerIDMaps[server] = _AllocDMAP ()) == NullTable)
    FatalError ("Out of memory ! Can't install ID map for server %d !\n",
		server);

  for (i = 0; i < HASHSIZE; i++)
    ServerIDMaps[server][i] = NullIDMapPtr;
}

void
InsertID (id, alias, server, whose)
  XID id, alias;
  int server, whose;
{
  IDMapPtr *bucket,		/* pointer to hash table bucket */
   newentry;			/* pointer to new map entry */

  if (ServerIDMaps[server] == NullTable)
    /* -- create it -- */
    _InstallIDMap (server);

  /* -- find the right place -- */
  bucket = ServerIDMaps[server] + HashID (id, whose);

  for (newentry = *bucket; newentry != NullIDMapPtr;
       newentry = newentry->next)

    /*
     * it has been inserted before, or entry is unused
     * Overwrite old values
     */
    if (!newentry->used ||
	newentry->id == id && newentry->whose == whose)
      break;

  if (newentry == NullIDMapPtr) {
    /* create , fill and insert the new entry */
    if ((newentry = _AllocEntry ()) == NullIDMapPtr)
      FatalError ("Out of memory ! Can't insert ID %u in server map %d !\n",
		  id, server);
    newentry->next = *bucket;
    *bucket = newentry;
  }

  newentry->id = id;
  newentry->alias = alias;
  newentry->whose = whose;
  newentry->used = True;
}

void
RemoveID (id, server, whose)
  XID id;
  int server, whose;
{
  IDMapPtr curr;		/* pointer to the entry that has to be
				 * deleted */
  register int i;

  if (ServerIDMaps[server] == NullTable) {
    ErrorF ("You tried to remove ID %u from the empty map %d\n", id, server);
    return;
  }
  for (i = 0; i < 2; i++) {

    /* find the right place in the table */
    curr = ServerIDMaps[server][HashID (id, whose)];

    if (curr == NullIDMapPtr) {
      ErrorF ("ID %u not found in ID map %d\n", id, server);
      return;
    }

    /* search through the list */
    for (; curr != NullIDMapPtr; curr = curr->next)
      if (curr->id == id && curr->whose == whose) {
	/* we've found it --> update the list */
	/* set values for second round */

	id = curr->alias;
	whose = (whose == FromClient) ? FromServer : FromClient;
	curr->used = False;
	break;
      }
    if (curr == NullIDMapPtr) {
      /* -- we failed ! -- */
      ErrorF ("ID %u not found in ID map %d\n", id, server);
      return;
    }
  }
}

void
DeleteIDMap (server)
  int server;
{
  if (ServerIDMaps[server] != NullTable)
    _FreeIDMAP (ServerIDMaps[server]);
  ServerIDMaps[server] = NullTable;
}

XID
MapID (id, server, whose)
  XID id;
  int server, whose;
{
  IDMapPtr entry;

  if ((id == 0) || (id == InvalidID))
    return (id);
  if (ServerIDMaps[server] == NullTable) {
    ErrorF ("Oops ! Can't map ID %u ( ID map %d empty ) !\n", id,
	    server);
    return (InvalidID);
  }

  /* find the right place */
  entry = ServerIDMaps[server][HashID (id, whose)];

  /* scan the list */
  for (; entry != NullIDMapPtr; entry = entry->next)
    if (entry->id == id && entry->whose == whose)
      /* -- we've found it -- */
      return (entry->alias);

  return (id);
}

XID
MapIDTryHard (id, whose)
  XID id;
  int whose;
{
  register int i;
  register XID mapped_id;

  for (i = 0; i < MAXSOCKS; i++)
    if (Connections[i] != (ClientPtr) NULL) {
      mapped_id = MapID (id, ConnectionNumber (XDisplay (Connections[i])),
			 whose);
      if (mapped_id != id)
	break;
    }
  return (mapped_id);
}
