/*
 * shmqueue.c -- shared memory linked lists
 *
 * Identification:
 *	$Header: /private/postgres/src/storage/ipc/RCS/shmqueue.c,v 1.3 1992/04/13 19:00:55 mer Exp $
 *
 * Package for managing doubly-linked lists in shared memory.
 * The only tricky thing is that SHM_QUEUE will usually be a field 
 * in a larger record.  SHMQueueGetFirst has to return a pointer
 * to the record itself instead of a pointer to the SHMQueue field
 * of the record.  It takes an extra pointer and does some extra
 * pointer arithmetic to do this correctly.
 *
 * NOTE: These are set up so they can be turned into macros some day.
 */
#include "tmp/postgres.h"
#include "storage/shmem.h"
#include "utils/log.h"

/*
 * ShmemQueueInit -- make the head of a new queue point
 * 	to itself
 */
SHMQueueInit(queue)
SHM_QUEUE	*queue;
{
  Assert(SHM_PTR_VALID(queue));
  (queue)->prev = (queue)->next = MAKE_OFFSET(queue);
}

/*
 * SHMQueueIsDetached -- TRUE if element is not currently
 *	in a queue.
 */
SHMQueueIsDetached(queue)
SHM_QUEUE 	*queue;
{
  Assert(SHM_PTR_VALID(queue));
  return ((queue)->prev == INVALID_OFFSET);
}

/*
 * SHMQueueElemInit -- clear an element's links
 */
SHMQueueElemInit(queue)
SHM_QUEUE 	*queue;
{
  Assert(SHM_PTR_VALID(queue));
  (queue)->prev = (queue)->next = INVALID_OFFSET;
}

/*
 * SHMQueueDelete -- remove an element from the queue and
 * 	close the links
 */
SHMQueueDelete(queue)
SHM_QUEUE	*queue;
{
  SHM_QUEUE *nextElem = (SHM_QUEUE *) MAKE_PTR((queue)->next);
  SHM_QUEUE *prevElem = (SHM_QUEUE *) MAKE_PTR((queue)->prev);

/*
  dumpQ(queue);
*/

  Assert(SHM_PTR_VALID(queue));
  Assert(SHM_PTR_VALID(nextElem));
  Assert(SHM_PTR_VALID(prevElem));
  prevElem->next =  (queue)->next;
  nextElem->prev =  (queue)->prev;

/*
  dumpQ((SHM_QUEUE *)MAKE_PTR(queue->prev));
*/

}

dumpQ(q)
SHM_QUEUE	*q;
{
    char elem[16];
    char buf[512];
    SHM_QUEUE	*start = q;
    int count = 0;

    sprintf(buf, "q prevs: %x", MAKE_OFFSET(q));
    q = (SHM_QUEUE *)MAKE_PTR(q->prev);
    while (q != start)
    {
	sprintf(elem, "--->%x", MAKE_OFFSET(q));
	strcat(buf, elem);
	q = (SHM_QUEUE *)MAKE_PTR(q->prev);
	if (q->prev == MAKE_OFFSET(q))
	    break;
	if (count++ > 40)
	{
	    strcat(buf, "BAD PREV QUEUE!!");
	    break;
	}
    }
    sprintf(elem, "--->%x", MAKE_OFFSET(q));
    strcat(buf, elem);
    elog(DEBUG, buf);

    sprintf(buf, "q nexts: %x", MAKE_OFFSET(q));
    count = 0;
    q = (SHM_QUEUE *)MAKE_PTR(q->next);
    while (q != start)
    {
	sprintf(elem, "--->%x", MAKE_OFFSET(q));
	strcat(buf, elem);
	q = (SHM_QUEUE *)MAKE_PTR(q->next);
	if (q->next == MAKE_OFFSET(q))
	    break;
	if (count++ > 10)
	{
	    strcat(buf, "BAD NEXT QUEUE!!");
	    break;
	}
    }
    sprintf(elem, "--->%x", MAKE_OFFSET(q));
    strcat(buf, elem);
    elog(DEBUG, buf);
}

/*
 * SHMQueueInsertHD -- put elem in queue between the queue head
 *	and its "prev" element.
 */
SHMQueueInsertHD(queue,elem)
SHM_QUEUE *queue,*elem;
{
  SHM_QUEUE *prevPtr = (SHM_QUEUE *) MAKE_PTR((queue)->prev);
  SHMEM_OFFSET	elemOffset = MAKE_OFFSET(elem);

  Assert(SHM_PTR_VALID(queue));
  Assert(SHM_PTR_VALID(elem));

  (elem)->next = prevPtr->next;
  (elem)->prev = queue->prev;
  (queue)->prev = elemOffset;
  prevPtr->next = elemOffset;
}

SHMQueueInsertTL(queue,elem)
SHM_QUEUE *queue,*elem;
{
  SHM_QUEUE *nextPtr = (SHM_QUEUE *) MAKE_PTR((queue)->next);
  SHMEM_OFFSET	elemOffset = MAKE_OFFSET(elem);

  Assert(SHM_PTR_VALID(queue));
  Assert(SHM_PTR_VALID(elem));

/*
  dumpQ(queue);
*/

  (elem)->prev = nextPtr->prev;
  (elem)->next = queue->next;
  (queue)->next = elemOffset;
  nextPtr->prev = elemOffset;

/*
  dumpQ(queue);
*/
}

/*
 * SHMQueueFirst -- Get the first element from a queue
 *
 * First element is queue->next.  If SHMQueue is part of
 * a larger structure, we want to return a pointer to the
 * whole structure rather than a pointer to its SHMQueue field.
 * I.E. struct {
 *	int 		stuff;
 *	SHMQueue 	elem;
 * } ELEMType; 
 * when this element is in a queue (queue->next) is struct.elem.
 * nextQueue allows us to calculate the offset of the SHMQueue
 * field in the structure.
 *
 * call to SHMQueueFirst should take these parameters:
 *
 *   &(queueHead),&firstElem,&(firstElem->next)
 *
 * Note that firstElem may well be uninitialized.  if firstElem
 * is initially K, &(firstElem->next) will be K+ the offset to
 * next.
 */
SHMQueueFirst(queue,nextPtrPtr,nextQueue)
SHM_QUEUE	*queue;
Addr 	*nextPtrPtr;
SHM_QUEUE	*nextQueue;
{
  SHM_QUEUE *elemPtr = (SHM_QUEUE *) MAKE_PTR((queue)->next);

  Assert(SHM_PTR_VALID(queue));
  *nextPtrPtr = (Addr) (((unsigned) *nextPtrPtr) +
    ((unsigned) elemPtr) - ((unsigned) nextQueue)); 

  /*
  nextPtrPtr a ptr to a structure linked in the queue
  nextQueue is the SHMQueue field of the structure
  *nextPtrPtr - nextQueue is 0 minus the offset of the queue 
  	field n the record 
  elemPtr + (*nextPtrPtr - nexQueue) is the start of the
  	structure containing elemPtr.
  */
}

/*
 * SHMQueueDoLRU -- move elem to the front of the list
 */
SHMQueueDoLRU(queue,elem)
SHM_QUEUE	*queue;
SHM_QUEUE	*elem;
{
  Assert(SHM_PTR_VALID(queue));

  /* do not LRU if the element is currently detached from the queue */
  if ((elem)->prev == INVALID_OFFSET)
    return;

  SHMQueueDelete(elem);
  SHMQueueInsertTL(queue,elem);
}


SHMQueueCheck(queue)
SHM_QUEUE	*queue;
{
  SHM_QUEUE *nextElem = (SHM_QUEUE *) MAKE_PTR((queue)->next);
  SHM_QUEUE *prevElem = (SHM_QUEUE *) MAKE_PTR((queue)->prev);

/*
  printf("curr: %x next %x prev %x: prev.next %x next.prev %x\n",
	 MAKE_OFFSET(queue),queue->next,queue->prev,
	 nextElem->prev,prevElem->next);
*/
  Assert(nextElem->prev == MAKE_OFFSET(queue));
  Assert(prevElem->next == MAKE_OFFSET(queue));
}

/*
 * SHMQueueEmpty -- TRUE if queue head is only element, FALSE otherwise
 */
SHMQueueEmpty(queue)
SHM_QUEUE       *queue;
{
  Assert(SHM_PTR_VALID(queue));

  if (queue->prev == MAKE_OFFSET(queue)) 
  {
    Assert(queue->next = MAKE_OFFSET(queue));
    return(TRUE);
  }
  return(FALSE);
}
