/*
 * libsal/fileman/all-systems/bitmap_cache.c
 * Support for free/busy bitmap
 * 
 * Part of the System Abstraction Layer
 */

/*
 * HOW IT WORKS
 * 
 * Each file is divided up into fixed-size blocks.
 * 
 * Each block is either 64 bytes, 128 bytes, 256 bytes, 512 bytes, 1K,
 * or a multiple of 1K in size.
 * 
 * All we care about is whether a block is free, or in use.  What it is
 * in use for is not our problem - that belongs to the layer above.
 * 
 * In order to know whether a block is free or in use, we employ the
 * classic bitmap approach, as found within countless other similiar
 * systems.  A set bit means that the block is in use, a clear bit
 * means that the block is free.  The contents of a free block are
 * best considered as undefined.
 * 
 * At the start of the file, we allocate 1K for internal use by this
 * subsystem, and then allocate a 1K bitmap.  If the file grows
 * beyond the end of the bitmap's range, we simply slap in another
 * bitmap at that point, and continue.
 * 
 * The functions in this file support the loading and saving of
 * these bitmaps, transparently using the generic skiplist support
 * in libsal to implement a write-through cache.
 */

#include <libsal/fileman/internal.h>
#include <libsal/license.h>
#include <libsal/libsal.h>

/* static function declarations */
static size_t sal_fileman_LocateBitmap(int);

/* -------------------------------------------------------------------------
 * sal_fileman_LocateBitmap()
 * Given a bitmap number, returns the offset into the file where the 1K
 * bitmap block starts.
 */

static size_t sal_fileman_LocateBitmap(int iBitmap)
{
  return (1024 + (iBitmap * (8096 * pstFile->ulPageSize)));
}

/* -------------------------------------------------------------------------
 * sal_fileman_BitmapCacheComp()
 * Compares two entries in the bitmap cache, and indicates their order.
 * 
 * Returns -1 if pstBlock1 < pstBlock2
 * Returns 0 if both blocks equal
 * Returns 1 if pstBlock1 > pstBlock2
 */

static int sal_fileman_BitmapCacheComp(void * pstBlock1, void * pstBlock2)
{
  sal_fileman_bitmap pstBitmap1;
  sal_fileman_bitmap pstBitmap2;
  
  /* NOTE
   * 
   * NULL pointers are valid inputs, and must be handled accordingly
   */
  
  if (pstBlock1 == NULL)
    return 1;
  
  if (pstBlock2 == NULL)
    return -1;
  
  pstBitmap1 = (sal_fileman_bitmap *) pstBlock1;
  pstBitmap2 = (sal_fileman_bitmap *) pstBlock2;
  
  if (pstBitmap1->iNumber < pstBitmap2->iNumber)
    return -1;
  else if (pstBitmap1->iNumber > pstBitmap2->iNumber)
    return 1;
  else
    return 0;
}

/* -------------------------------------------------------------------------
 * initialise the bitmap cache
 */

void sal_fileman_InitBitmapCache(sal_fileman * pstFile)
{
  TEST_ARG(pstFile != NULL, 1);
  sal_skiplist_Init();
  
  pstFile->stBitmapCache = sal_skiplist_NewList(sal_fileman_BitmapCacheComp);
}

/* -------------------------------------------------------------------------
 * load a bitmap
 */

sal_fileman_bitmap * sal_fileman_LoadBitmap(sal_fileman * pstFile, int iBitmap)
{
  sal_fileman_bitmap stBitmap;
  sal_fileman_bitmap * pstCachedBitmap;
  size_t gLocation;
    
  TEST_ARG(pstFile != NULL, 1);
  TEST_ARG(iBitmap >= 0, 2);
  
  /* first things first - check the cache */
  
  stBitmap.iNumber = iBitmap;
  if (sal_skiplist_Search(pstFile->stBitmapCache, (void *) &stBitmap, (void **) &pstCachedBitmap))
    return pstCachedBitmap;
  
  /* 
   * at this point, we know that what we want isn't in the cache.
   * So, it is now our job to go and put it there
   */
  
  pstCachedBitmap = (sal_fileman_bitmap *) malloc (sizeof (sal_fileman_bitmap));
  gLocation = sal_fileman_LocateBitmap(iBitmap);
  
