/*static char *SCCSID = "@(#)mem.c      6.1 90/11/18";*/
/*static char *SCCSID = "@(#)mem.c 13.4 90/03/05";*/
/***    MEM.C - Routines to allocate and deallocate memory
 *
 *      CHKDSK
 *      Gregory A. Jones
 *      Copyright 1988 Microsoft Corporation
 *
 *      Modification history:
 *      G.A. Jones      05/26/88        Original for Pinball 1.0.
 *
 *      69658  06/15/93  rjp  Ported OS/2 1.3 fix from defect 5466:
 *                            Memory reduction. Reduce the chk_disk_map and
 *                            chk_db_usemap from 8 bits per sector to 2 bits
 *                            per sector.
 */

#include "ldefs.h"
#include "ltypes.h"
#include "globals.h"
#include <pr_chk.h>


/***    chk_allocate_maps - allocate bit maps and disk map
 *
 *      This function is called to allocate memory for both copies of
 *      the bitmap, as well as CHKDSK's internal disk map.  The bit
 *      maps are not initialized, since they will be filled with disk
 *      data;  the disk map is initialized to DISK_UNKNOWN.
 *
 *      Memory for the bitmaps is allocate one block (2K) overlong so
 *      that reading the maps in will not overflow them.
 *
 *      This routine also allocates the filesystem-style cache for
 *      file creation and deletion routines to use.
 *
 *      chk_allocate_maps (nsects)
 *
 *      ENTRY           nsects - number of sectors in partition
 *
 *      EXIT            No return value
 *
 *      CALLS           halloc
 *                      memset
 *
 *      EFFECTS         Allocates memory
 *                      Initializes disk map
 *
 *      WARNINGS        May exit program if insufficient memory
 */
void chk_allocate_maps (nsects)
  ULONG nsects;
{
  ULONG i;
  /* Divide size by 4 (8bits to 2bits) and add one because zero based                  * 69658 -rjp */
  ULONG ulDiskMapSize = ( nsects >> 2 ) + 1;                                          /* 69658 -rjp */
  USHORT usDBUseMapSize = ( ( MAX_DBS_PER_DIRBAND * SECTORS_PER_DIRBLK ) >> 2 ) + 1;  /* 69658 -rjp */

#ifdef TRACE
  fprintf (stderr, "chk_allocate_maps ()\n");
  fflush (stderr);
#endif

  i = (nsects - 1L) / ((ULONG)BYTES_PER_BITMAP * 8L) + 2L;
  if (((bitmap   = halloc (i, 2048)) == NULL) ||
#ifdef TWO_MAPS
      ((bitmap2  = halloc (i, 2048)) == NULL) ||
#endif
      ((db_bitmap=_fmalloc (SPB*SECSIZE)) == NULL) ||
      ((chk_db_usemap=_fmalloc ( usDBUseMapSize )) == NULL) ||                                       /* 69658 -rjp */

      ((chk_disk_map = halloc ( ulDiskMapSize, sizeof (UCHAR))) == NULL))                            /* 69658 -rjp */
    chk_exit_error (INSF_MEM_ERROR);

  for (i=0L; i + 32768L < ulDiskMapSize; i += 32768L)          /* clear out the map */               /* 69658 -rjp */
    memset (&chk_disk_map [i], DISK_UNKNOWN, 32768U);   /* in 32K chunks */
  if (ulDiskMapSize - i)                                                                             /* 69658 -rjp */
    memset (&chk_disk_map [i], DISK_UNKNOWN, (size_t)(ulDiskMapSize - i)); /* clear last fraction */ /* 69658 -rjp */
  memset (chk_db_usemap, DISK_UNKNOWN, usDBUseMapSize );                                             /* 69658 -rjp */

  initcache ();
}

/***    chk_free_memtree - deallocate a lost-block node and its descendants
 *
 *      This function is called to deallocate a lost-block node and
 *      its descendants.  It first calls itself recursively on the
 *      node's child list, and then walks the "next" list.
 *
 *      It is safe to call this function with a NULL pointer, since
 *      the FOR loop used will simply fall through in that case.
 *
 *      chk_free_memtree (mem)
 *
 *      ENTRY           mem - pointer to node to deallocate
 *
 *      EXIT            No return value
 *
 *      CALLS           chk_free_memtree (recursive)
 *
 *      EFFECTS         Deallocates memory
 */
void chk_free_memtree (mem)
   mem_blk *mem;
{
   mem_blk *p, *q;

#ifdef TRACE
  fprintf (stderr, "chk_free_memtree ()\n");
  fflush (stderr);
#endif

  for (p=mem; p; ) {                    /* for all nodes at this level */
    chk_free_memtree (p->childlist);    /* delete this node's children */
    q = p;                              /* save a pointer to this node */
    p = p->next;                        /* move to the next one */
    FREE_BLOCK (q);                     /* deallocate the one we just did */
  }
}

/***    chk_free_all_memory - deallocate all allocated structures
 *
 *      This function is called to free all global allocated memory
 *      structures, such as the "lost block" tree.  It calls a lower-
 *      level function to deallocate individual items.
 *
 *      This function also flushes and deallocates the filesystem-style
 *      cache used by file creation and deletion routines.
 *
 *      chk_free_all_memory (void)
 *
 *      ENTRY           No parameters
 *
 *      EXIT            No return value
 *
 *      CALLS           chk_free_memtree
 *
 *      EFFECTS         Deallocates all global memory structures
 *
 *      WARNINGS        Global variables are made NULL
 */
void chk_free_all_memory (void)
{
#ifdef TRACE
  fprintf (stderr, "chk_free_all_memory ()\n");
  fflush (stderr);
#endif

  FREE_MAP (bitmap);
#ifdef TWO_MAPS
  FREE_MAP (bitmap2);
#endif
  FREE_MAP (chk_disk_map);
  _ffree (db_bitmap);
  _ffree (chk_db_usemap);

  chk_free_memtree (chk_lost);

  flushcache ();
  freecache ();
}

/***    chk_allocate_memblk - allocate a structure to chain lost fragments
 *
 *      This function is called to allocate a mem_blk structure --
 *      the structure used to contain memory images of lost disk
 *      fragments and chain them together.  The structure's fields,
 *      apart from the disk image itself, are initialized to sane
 *      values.
 *
 *      chk_allocate_memblk ()
 *
 *      ENTRY           No parameters
 *
 *      EXIT            Returns a pointer to the allocated structure
 *
 *      CALLS           _fmalloc
 *
 *      WARNINGS        May return a NULL pointer if insufficient memory
 *
 *      EFFECTS         Allocates memory
 */
 mem_blk *chk_allocate_memblk ()
{
   mem_blk *p;

#ifdef TRACE
  fprintf (stderr, "chk_allocate_memblk ()\n");
  fflush (stderr);
#endif

  if ((p=_fmalloc (sizeof ( mem_blk))) == NULL)
    return (NULL);

  p->next = NULL;
  p->psn = 0L;
  p->children = 0;
  p->parent = 0;
  p->childlist = NULL;
  return (p);
}

/***    chk_enter_recursive - check stack depth for recursion
 *
 *      This function is called by the various recursive functions in
 *      CHKDSK.  It checks to see that the current stack depth will
 *      be deep enough for another call to that routine.  It depends
 *      on the global variable "chk_stackdepth", which starts out at
 *      STACKSIZE-5000.  If this variable becomes less than twice its
 *      parameter, an error code is returned so that the program can
 *      stop recursion before the C compiler's internal stack checking
 *      terminates the program.
 *
 *      The parameter to this function should be the number of bytes used
 *      in parameters and variables.  Register variables should be included,
 *      since the compiler will probably have to expend stack to save them.
 *      Remember also that char and UCHAR variables will have an extra byte
 *      of alignment padding on them.
 *
 *      chk_enter_recursive (depth)
 *
 *      ENTRY           depth - number of bytes in parameters and variables
 *
 *      EXIT            Returns zero if enough stack, non-zero if not
 */
USHORT chk_enter_recursive (depth)
  USHORT depth;
{
  chk_stackdepth -= depth + 4;          /* 2-byte call plus PUSH BP = 4 bytes */
#ifdef TRACE
  if (chk_stackdepth < chk_minstk)      /* deeper than we've been before */
    chk_minstk = chk_stackdepth;        /* save new minimum for reference */
#endif
  return (chk_stackdepth < 2*depth);
}

/***    exit_recursive - adjust stack depth when exiting procedure
 *
 *      This function is called when a recursive function is about to
 *      return to its caller.  It adds to the "chk_stackdepth" variable,
 *      simulating adding to the stack pointer register.
 *
 *      The parameter to this function should be the number of bytes
 *      taken up in parameters and local variables for the recursive
 *      function.  It should be the same number that is passed to
 *      chk_enter_recursive for that function.  Of course, different
 *      recursive functions may use different values.
 *
 *      chk_exit_recursive (depth)
 *
 *      ENTRY           depth - number of bytes used by function
 *
 *      EXIT            No return value
 */
void chk_exit_recursive (depth)
  USHORT depth;
{
  chk_stackdepth += depth + 6;          /* 4-byte call plus PUSH BP = 6 bytes */
}
