/*-------------------------------------------------------------------------
 *
 * freespace.c
 *	  POSTGRES free space map for quickly finding free space in relations
 *
 *
 * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * IDENTIFICATION
 *	  $Header: /cvsroot/pgsql/src/backend/storage/freespace/freespace.c,v 1.1 2001/06/27 23:31:39 tgl Exp $
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "storage/freespace.h"
#include "storage/itemid.h"
#include "storage/shmem.h"


/*
 * Shared free-space-map objects
 *
 * Note: we handle pointers to these items as pointers, not as SHMEM_OFFSETs.
 * This assumes that all processes accessing the map will have the shared
 * memory segment mapped at the same place in their address space.
 */
typedef struct FSMHeader FSMHeader;
typedef struct FSMRelation FSMRelation;
typedef struct FSMChunk FSMChunk;

/* Header for whole map */
struct FSMHeader
{
	HTAB	   *relationHash;	/* hashtable of FSMRelation entries */
	FSMRelation *relationList;	/* FSMRelations in order by recency of use */
	int			numRelations;	/* number of FSMRelations now in use */
	FSMChunk   *freeChunks;		/* linked list of currently-free chunks */
};

/*
 * Per-relation struct --- this is an entry in the shared hash table.
 * The hash key is the RelFileNode value (hence, we look at the physical
 * relation ID, not the logical ID, which is appropriate).
 */
struct FSMRelation
{
	RelFileNode	key;			/* hash key (must be first) */
	FSMRelation *nextRel;		/* next rel in order by recency of use */
	FSMRelation *priorRel;		/* prior rel in order by recency of use */
	FSMChunk   *relChunks;		/* linked list of page info chunks */
};

#define SHMEM_FSMHASH_KEYSIZE  sizeof(RelFileNode)
#define SHMEM_FSMHASH_DATASIZE (sizeof(FSMRelation) - SHMEM_FSMHASH_KEYSIZE)

#define CHUNKPAGES  32			/* each chunk can store this many pages */

struct FSMChunk
{
	FSMChunk   *next;			/* linked-list link */
	int			numPages;		/* number of pages described here */
	BlockNumber	pages[CHUNKPAGES]; /* page numbers within relation */
	ItemLength	bytes[CHUNKPAGES]; /* free space available on each page */
};


SPINLOCK	FreeSpaceLock;		/* in Shmem or created in
								 * CreateSpinlocks() */

int		MaxFSMRelations;		/* these are set by guc.c */
int		MaxFSMPages;

static FSMHeader *FreeSpaceMap;	/* points to FSMHeader in shared memory */


/*
 * InitFreeSpaceMap -- Initialize the freespace module.
 *
 * This must be called once during shared memory initialization.
 * It builds the empty free space map table.  FreeSpaceLock must also be
 * initialized at some point, but is not touched here --- we assume there is
 * no need for locking, since only the calling process can be accessing shared
 * memory as yet.  FreeSpaceShmemSize() was called previously while computing
 * the space needed for shared memory.
 */
void
InitFreeSpaceMap(void)
{
	HASHCTL		info;
	FSMChunk   *chunks,
			   *prevchunk;
	int			nchunks;

	/* Create table header */
	FreeSpaceMap = (FSMHeader *) ShmemAlloc(sizeof(FSMHeader));
	if (FreeSpaceMap == NULL)
		elog(FATAL, "Insufficient shared memory for free space map");
	MemSet(FreeSpaceMap, 0, sizeof(FSMHeader));

	/* Create hashtable for FSMRelations */
	info.keysize = SHMEM_FSMHASH_KEYSIZE;
	info.datasize = SHMEM_FSMHASH_DATASIZE;
	info.hash = tag_hash;

	FreeSpaceMap->relationHash = ShmemInitHash("Free Space Map Hash",
											   MaxFSMRelations / 10,
											   MaxFSMRelations,
											   &info,
											   (HASH_ELEM | HASH_FUNCTION));

	if (!FreeSpaceMap->relationHash)
		elog(FATAL, "Insufficient shared memory for free space map");

	/* Allocate FSMChunks and fill up the free-chunks list */
	nchunks = (MaxFSMPages - 1) / CHUNKPAGES + 1;

	chunks = (FSMChunk *) ShmemAlloc(nchunks * sizeof(FSMChunk));
	if (chunks == NULL)
		elog(FATAL, "Insufficient shared memory for free space map");

	prevchunk = NULL;
	while (nchunks-- > 0)
	{
		chunks->next = prevchunk;
		prevchunk = chunks;
		chunks++;
	}
	FreeSpaceMap->freeChunks = prevchunk;
}


int
FreeSpaceShmemSize(void)
{
	int			size;
	int			nchunks;

	/*
	 * There is no point in allowing less than one "chunk" per relation,
	 * so force MaxFSMPages to be at least CHUNKPAGES * MaxFSMRelations.
	 */
	Assert(MaxFSMRelations > 0);
	if (MaxFSMPages < CHUNKPAGES * MaxFSMRelations)
		MaxFSMPages = CHUNKPAGES * MaxFSMRelations;

	/* table header */
	size = MAXALIGN(sizeof(FSMHeader));

	/* hash table, including the FSMRelation objects */
	size += hash_estimate_size(MaxFSMRelations,
							   SHMEM_FSMHASH_KEYSIZE,
							   SHMEM_FSMHASH_DATASIZE);

	/* FSMChunk objects */
	nchunks = (MaxFSMPages - 1) / CHUNKPAGES + 1;

	size += MAXALIGN(nchunks * sizeof(FSMChunk));

	return size;
}


void
FreeSpaceMapForgetRel(RelFileNode *rel)
{
}


#ifdef FREESPACE_DEBUG
/*
 * Dump contents of freespace map for debugging.
 *
 * We assume caller holds the FreeSpaceLock, or is otherwise unconcerned
 * about other processes.
 */
void
DumpFreeSpace(void)
{
}

#endif	 /* FREESPACE_DEBUG */
