/*static char *SCCSID = "@(#)msp32.h	6.4 92/03/24";*/

/********************** START OF SPECIFICATIONS *******************************
*
*   SOURCE FILE NAME:   Msp32.h
*
*   DESCRIPTIVE NAME:   32 bit Memory Suballocation Package include file
*
*   COPYRIGHT: (TOP SOURCE FILE ONLY)
*
*   STATUS: (TOP SOURCE FILE ONLY)
*
*   ENTRY POINTS:       mspRoundUp,       mspRequestSem
*                       mspPgRndUp,       mspReleaseSem
*                       mspObjShared,     mspSetExcept
*                       mspSerialize,     mspIncrFence
*                       mspSparseObj,     mspDecrFence
*                       mspNotShrObj,     mspFreeParmTest
*
*   EXTERNAL REFERENCES:    Dos32ReleaseMutexSem
*                           Dos32RequestMutexSem,
*                           Dos32SetMem,
*                           Dos32SetExceptionHandler,
*                           setjmp
* History:
*   geb  9/15/89 - PTR b707470 - initial creation (split from msp32.c)
*
************************** END OF SPECIFICATIONS *****************************/

#include <r3except.h>

#define DOSSUB_NO_SHRINK           0x10

/***************** MSP Internal Structures *******************/

typedef struct MspEntry  {        /* Free Block Descriptor             */
    struct MspEntry *MspNext;     /* Link to next free block           */
    ULONG  MspSize;               /* Size of this free block           */
} MSPE, * PMSPE;

typedef struct MspHeader  {       /* MSP object header record          */
    MSPE  MspHdrEntry;            /* Link to next free block           */
    ULONG MspEndObj;              /* end offset of msp object          */
    HMTX  MspSemaphore;           /* Semaphore (serialization)         */
    MSPE *MspHintPtr;             /* Pointer to last block touched     */
    ULONG MspHintSize;            /* Size of last block touched        */
    MSPE *MspHPrevPtr;            /* Ptr to PrevBlk of last touched    */
    ULONG MspFence;               /* Committed page Fence              */
    ULONG MspFlags;               /* Flags for MSP                     */
    char  MspReserved[28];        /* total header size = 64 bytes      */
} MSPH, * PMSPH;

#define AnchorNext(x)  (x)->MspHdrEntry.MspNext       /* anchor free block  */
#define AnchorSize(x)  (x)->MspHdrEntry.MspSize       /* anchor free blk sz */
#define mspObjSize(h)  ((h)->MspEndObj - (ULONG)(h))  /* size of msp object */

typedef struct msp_exception_and_jmp_buf {
   EXCEPTIONREGISTRATIONRECORD MspExceptBuf; /* buffer for Exception */
                                             /* Registration struct  */
   jmp_buf    MspJmpBuf;          /* buffer used by setjmp and longjmp  */
   ULONG      MspSemaphore;       /* handle to free if error occurred   */
}  MSPXJBUF;


/************** Internal Function Declarations *******************/

ULONG _cdecl msp_exception_handler (PEXCEPTIONREPORTRECORD,
                             PEXCEPTIONREGISTRATIONRECORD,
                             PCONTEXTRECORD,
                             PVOID);
ULONG msp_init_object       (PMSPH, ULONG ,ULONG);
ULONG msp_grow_object       (PMSPH, ULONG ,ULONG, PULONG);

/********************   Local defines   **************************/

#define MSPPAGMSK       0xFFF
#define MULT8MSK        0x7
#define DESCSIZ         (sizeof (struct MspEntry))
#define MINSIZ          DESCSIZ                       /* Min alloc/free size */
#define ROOTSIZ         (sizeof (struct MspHeader))   /* Overhead            */
#define MSP_COMMIT_MEM  (PAG_COMMIT | PAG_READ | PAG_WRITE | PAG_EXECUTE)
#define MSP_FLAG_MSK    (DOSSUB_SPARSE_OBJ | DOSSUB_SERIALIZE |      \
                         DOSSUB_INIT | DOSSUB_NO_SHRINK)
#define ENDLIST         0
#define SET_BADPARM    -1
#define SET_CORRUPTED  -2
#define MSP_SHARED      0x80000000    /* msp object in shared region */

#define MSP_MAX_FLAGS   (DOSSUB_SPARSE_OBJ | DOSSUB_SERIALIZE |             \
                         DOSSUB_INIT | DOSSUB_GROW | DOSSUB_NO_SHRINK)

extern VOID Unwind(PEXCEPTIONREGISTRATIONRECORD);

extern VOID RegisterEH ( PEXCEPTIONREGISTRATIONRECORD pHandler );

extern VOID DeRegisterEH ( VOID );


/******************   Local Macro Definitions  ******************/


/***
*   MACRO:    mspRoundUp
*
*   FUNCTION: given size, round up value (if neeeded) to next
*               multiple of 8
*
*   INPUT:    x - unsigned int size
*
*   RETURNS:  x rounded up (if needed) to the next multiple of 8)
*/

#define mspRoundUp(x)       (((x) + MULT8MSK) & ~MULT8MSK)


/***
*   MACRO:    mspPgRndUp
*
*   FUNCTION: given size, round up value (if neeeded) to next
*               multiple of 0x1000 (page boundary)
*
*   INPUT:    x - unsigned int offset
*
*   RETURNS:  x rounded up (if needed) to the next page boundary)
*/

#define mspPgRndUp(x)    (((x) + MSPPAGMSK) & ~MSPPAGMSK)


/***
*   MACRO:    mspObjShared
*
*   FUNCTION: given object's flags determine if in the private or shared region
*
*   INPUT:    flags returned by a DosQueryMem call
*
*   RETURNS:  if object in shared region return DC_SEM_SHARED
*             else  0
*/

#define mspObjShared(x)          (((x) & PAG_SHARED) ? DC_SEM_SHARED : 0)


/***
*   MACRO:    mspSerialize
*
*   FUNCTION: given a pointer to a Msp Header, test if serialize bit is set
*
*   INPUT:    Msp header pointer
*
*   RETURNS:  if serialize bit is set return 1 (true)
*             else  0 (false)
*/

#define mspSerialize(x)           ((x)->MspFlags & DOSSUB_SERIALIZE)


/***
*   MACRO:    mspQRWFlags
*
*   FUNCTION: given object's flags determine if read/write/non-sparse
*               and no guard
*
*   INPUT:    flags returned by a DosQueryMem call
*
*   RETURNS:  if not read/write/commit/no guard return 1 (true)
*             else  0 (false)
*/

#define mspQRWFlags(x) (((x) & (PAG_COMMIT|PAG_READ|PAG_WRITE)) \
                           != (PAG_COMMIT | PAG_READ | PAG_WRITE))


/***
*   MACRO:    mspSparseObj
*
*   FUNCTION: given a pointer to a Msp Header, test if sparse bit is set
*
*   INPUT:    Msp header pointer
*
*   RETURNS:  if sparse bit is set return 1
*             else  0
*/

#define mspSparseObj(x)  ((x)->MspFlags & DOSSUB_SPARSE_OBJ)

/***
*   MACRO:    mspShrinkable
*
*   FUNCTION: given a pointer to a Msp Header, test the DOSSUB_NO_SHRINK bit
*
*   INPUT:    Msp header pointer
*
*   RETURNS:  if NO_SHRINK bit is set return 0
*             else  return 1
*/
#define mspShrinkable(x)                    \
           (((x)->MspFlags & DOSSUB_NO_SHRINK) == 0)


/***
*   MACRO:    mspNotShrObj
*
*   FUNCTION: given a pointer to a Msp Header, test if object resides in
*                 the private region  (not in shared region)
*
*   INPUT:    Msp header pointer
*
*   RETURNS:  if object in shared region return 0 (false)
*             else  1 (true)
*/

#define mspNotShrObj(x)          (((x)->MspFlags & MSP_SHARED) ? 0 : 1)


/***
*   MACRO:    mspRequestSem
*
*   FUNCTION: request msp semaphore
*
*   INPUT:    semaphore handle
*
*   RETURNS:  if error ERROR_DOSSUB_CORRUPTED
*             else     0
*/

#define mspRequestSem(x)  ((DosRequestMutexSem ((x), (ULONG) -1))     \
                                ?  ERROR_DOSSUB_CORRUPTED : NO_ERROR)

/***
*   MACRO:    mspReleaseSem
*
*   FUNCTION: release msp semaphore, if an error occurs set rc to
*               CORRUPTED, if no error occurs (r) is left unchanged
*
*   INPUT:    h - pointer to a Msp Header
*             r - return code (only set on error)
*
*   RETURNS:  r set to ERROR_DOSSUB_CORRUPTED if error
*             else     r is left unchanged
*/

#define mspReleaseSem(h,r)                                                 \
    if (mspSerialize(h))                                                   \
        if ((DosReleaseMutexSem(h->MspSemaphore)) != NO_ERROR)           \
            r=ERROR_DOSSUB_CORRUPTED;


/***
*   MACRO:    mspSetExcept
*
*   FUNCTION: setup the exception handler structure, call setjmp and call
*             SetExceptionHandler.
*
*    Note: This code is executed upon entry into the SubSet, SubAlloc,
*          SubFree and SubUnset Doscalls.
*          If an exception occurs then this code will be "longjmp"ed to,
*          the MspBuf.MspSemaphore field contains either a
*
*          -1 , meaning the Msp Object is not corrupted (bad parm)
*          -2 , the Msp Object is corrupted or
*           x , release the semaphore  using 'x' as the semaphore handle and
*                   return CORRUPTED
*
*   INPUT: pointer to a MSPXJBUF structure
*
*/

#define mspSetExcept(x)                                                      \
    {                                                                        \
        (x).MspExceptBuf.ExceptionHandler=&msp_exception_handler;         \
        if (setjmp((x).MspJmpBuf) != 0)                                      \
        {                                                                    \
            if ((x).MspSemaphore < SET_BADPARM)  /* then corrupted */        \
                return(ERROR_DOSSUB_CORRUPTED);                              \
            else                                /* else bad parm  */         \
                return(ERROR_INVALID_PARAMETER);                             \
        }                                                                    \
        RegisterEH((PEXCEPTIONREGISTRATIONRECORD)&(x));                         \
    }


/***
*   MACRO:    mspIncrFence
*
*   FUNCTION: is the object is sparse and the being allocated spands a new
*             page boundary(s), commit any necessary pages
*
*
*   INPUT: h - pointer to a msp header
*          n - ending offset of newly allocated block
*          r - return code (set only on error)
*
*   RETURNS: continues if no error
*            else jumps to adone (to UnsetExceptHandler and return rc)
*/

#define mspIncrFence(h,n,r)                                                \
  {                                                                        \
    if (mspSparseObj(h) && h->MspFence < (n))                              \
    {                                                                      \
        switch (DosSetMem((PVOID)h->MspFence,(n) -                         \
                          h->MspFence,MSP_COMMIT_MEM))                     \
        {                                                                  \
            case NO_ERROR:                                                 \
                h->MspFence = (n);                                         \
                break;                                                     \
            case ERROR_NOT_ENOUGH_MEMORY:                                  \
                r = ERROR_NOT_ENOUGH_MEMORY;                               \
                goto adone;                                                \
                break;                                                     \
            default:                                                       \
                r = ERROR_DOSSUB_CORRUPTED;                                \
                goto adone;                                                \
                break;                                                     \
        }                                                                  \
    }                                                                      \
  }


#define mspIncrFence2(h,r)                                            \
 {                                                                    \
    if (mspSparseObj(h) && ((h->MspEndObj + DESCSIZ) > h->MspFence))  \
    {                                                                 \
        switch (DosSetMem((PVOID)(h)->MspFence,0x1000,MSP_COMMIT_MEM))\
        {                                                             \
            case NO_ERROR:                                            \
                (h)->MspFence += 0x1000;                              \
                break;                                                \
            case ERROR_NOT_ENOUGH_MEMORY:                             \
                r = ERROR_NOT_ENOUGH_MEMORY;                          \
                goto exit_error1;                                     \
            default:                                                  \
                r = ERROR_DOSSUB_CORRUPTED;                           \
                goto exit_error1;                                     \
        }                                                             \
    }                                                                 \
 }



/***
*   MACRO:    mspDecrFence
*
*   FUNCTION: is the object is sparse and the being allocated spands a new
*             page boundary(s), commit any necessary pages
*
*
*   INPUT: h - pointer to a msp header
*          n - ending offset of descriptor of the newly freed block
*          p - pointer to msp entry being freed
*          r - return code (set only on error)
*
*   RETURNS: continues if no error
*            else jumps to fdone (to UnsetExceptHandler and return rc)
*/

#define mspDecrFence(h,n,p,r)                                              \
  {                                                                        \
    if ( mspSparseObj(h) && mspNotShrObj(h) && mspShrinkable(h) &&         \
         ( (((ULONG)p+p->MspSize) >= h->MspFence) ||                       \
           (((ULONG)p+p->MspSize) == h->MspEndObj) ) )                     \
    {                                                                      \
        if (h->MspFence > (n))                                             \
        {                                                                  \
            if ((DosSetMem ((PVOID)(n), (h->MspFence-(n)), PAG_DECOMMIT))  \
                    == NO_ERROR)                                           \
                 h->MspFence = (n);                                        \
            else                                                           \
            {                                                              \
                r = ERROR_DOSSUB_CORRUPTED;                                \
                goto fdone;                                                \
            }                                                              \
        }                                                                  \
    }                                                                      \
  }

/***
*   MACRO:    mspFreeParmTest
*
*   FUNCTION: Validate parameters for DosSubFree routine.  Test for
*        a) size == 0
*        b) block to free offset < end of msp header
*        c) size + offset overflow
*        d) size + offset > end of msp object
*             error if any are true
*
*   INPUT: h - pointer to a msp header
*          n - size of block to be freed
*          b - offset of block to be freed
*
*   RETURNS: 1 if error
*            otherwise 0
*/

#define mspFreeParmTest(h,n,b)                                  \
         ( (n) == 0)                                 ||         \
         (  (ULONG)(b)      <  (ULONG)(h)+ROOTSIZ)   ||         \
         ( ((ULONG)(b)+(n)) <  (ULONG)(b) )          ||         \
         ( ((ULONG)(b)+(n)) >  ((h)->MspEndObj))

