1264 lines
32 KiB
C
1264 lines
32 KiB
C
/*****************************************************************************
|
|
* *
|
|
* FREELIST.C *
|
|
* *
|
|
* Copyright (C) Microsoft Corporation 1995. *
|
|
* All Rights reserved. *
|
|
* *
|
|
******************************************************************************
|
|
* *
|
|
* Module Intent *
|
|
* *
|
|
* Free List manager functions. List can handle 8-byte file offsets and *
|
|
* 8-byte file lengths *
|
|
* *
|
|
******************************************************************************
|
|
* *
|
|
* Current Owner: davej *
|
|
*****************************************************************************/
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Created 07/17/95 - davej
|
|
* 3/05/97 erinfox Change errors to HRESULTS
|
|
*
|
|
*****************************************************************************/
|
|
|
|
static char s_aszModule[] = __FILE__; /* For error report */
|
|
|
|
#include <mvopsys.h>
|
|
#include <orkin.h>
|
|
#include <misc.h>
|
|
#include <mem.h>
|
|
#include <freelist.h>
|
|
|
|
/*****************************************************************************
|
|
* *
|
|
* Defines *
|
|
* *
|
|
*****************************************************************************/
|
|
|
|
|
|
/*****************************************************************************
|
|
* *
|
|
* Prototypes *
|
|
* *
|
|
*****************************************************************************/
|
|
|
|
/***************************************************************************
|
|
* *
|
|
* Private Functions *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func HFREELIST PASCAL FAR | FreeListInit |
|
|
* Create or read in a free list
|
|
*
|
|
* @parm WORD | wMaxBlocks |
|
|
* Number of free list entries
|
|
*
|
|
* @parm PHRESULT | phr |
|
|
* Error code return if return value is NULL
|
|
*
|
|
* @rdesc Returns handle to a FREELIST, otherwise NULL if error.
|
|
*
|
|
***************************************************************************/
|
|
|
|
PUBLIC HFREELIST PASCAL FAR EXPORT_API FreeListInit( WORD wMaxBlocks, PHRESULT phr)
|
|
{
|
|
HFREELIST hFreeList = NULL;
|
|
QFREELIST qFreeList = NULL;
|
|
|
|
if (!wMaxBlocks)
|
|
{
|
|
SetErrCode(phr, E_INVALIDARG);
|
|
return NULL;
|
|
}
|
|
|
|
if (!(hFreeList=_GLOBALALLOC(GMEM_ZEROINIT| GMEM_MOVEABLE,
|
|
sizeof(FREEITEM)*wMaxBlocks+sizeof(FREELISTHDR))))
|
|
{
|
|
SetErrCode(phr,E_OUTOFMEMORY);
|
|
return NULL;
|
|
}
|
|
|
|
if (!(qFreeList=_GLOBALLOCK(hFreeList)))
|
|
{
|
|
SetErrCode(phr,E_OUTOFMEMORY);
|
|
goto exit1;
|
|
}
|
|
|
|
qFreeList->flh.wMaxBlocks=wMaxBlocks;
|
|
|
|
_GLOBALUNLOCK(hFreeList);
|
|
|
|
return hFreeList;
|
|
|
|
exit1:
|
|
_GLOBALFREE(hFreeList);
|
|
return NULL;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func HFREELIST PASCAL FAR | FreeListInitFromMem |
|
|
* Initialize a freelist structure from a memory image. Memory image is
|
|
* always in Intel byte ordering format.
|
|
*
|
|
* @parm LPVOID | lpvMem |
|
|
* Pointer to memory image of free list
|
|
*
|
|
* @parm PHRESULT | phr |
|
|
* Error code return valid if return value is NULL
|
|
*
|
|
* @rdesc Returns handle to a FREELIST, otherwise NULL if error.
|
|
*
|
|
***************************************************************************/
|
|
|
|
PUBLIC HFREELIST PASCAL FAR EXPORT_API FreeListInitFromMem( LPVOID lpvMem, PHRESULT phr )
|
|
{
|
|
QFREELIST qFreeListMem;
|
|
QFREELIST qFreeList;
|
|
HFREELIST hFreeList = NULL;
|
|
WORD wMaxBlocks;
|
|
WORD wNumBlocks;
|
|
DWORD dwLostBytes;
|
|
|
|
if (!lpvMem)
|
|
{
|
|
SetErrCode(phr, E_INVALIDARG);
|
|
return NULL;
|
|
}
|
|
|
|
qFreeListMem = (QFREELIST)lpvMem;
|
|
wMaxBlocks = qFreeListMem->flh.wMaxBlocks;
|
|
wNumBlocks = qFreeListMem->flh.wNumBlocks;
|
|
dwLostBytes = qFreeListMem->flh.dwLostBytes;
|
|
|
|
// Mac-ify
|
|
wMaxBlocks = SWAPWORD(wMaxBlocks);
|
|
wNumBlocks = SWAPWORD(wNumBlocks);
|
|
dwLostBytes = SWAPLONG(wNumBlocks);
|
|
|
|
if (! wMaxBlocks )
|
|
{
|
|
SetErrCode(phr, E_ASSERT);
|
|
return NULL;
|
|
}
|
|
|
|
if (!(hFreeList=_GLOBALALLOC(GMEM_ZEROINIT| GMEM_MOVEABLE,
|
|
sizeof(FREEITEM)*wMaxBlocks+sizeof(FREELISTHDR))))
|
|
{
|
|
SetErrCode(phr,E_OUTOFMEMORY);
|
|
goto exit0;
|
|
}
|
|
|
|
if (!(qFreeList=_GLOBALLOCK(hFreeList)))
|
|
{
|
|
SetErrCode(phr,E_OUTOFMEMORY);
|
|
goto exit1;
|
|
}
|
|
|
|
QVCOPY( qFreeList, qFreeListMem, sizeof(FREELISTHDR) + wMaxBlocks * sizeof(FREEITEM));
|
|
|
|
#ifdef _BIG_E
|
|
{
|
|
QFREEITEM qCurrent = qFreeList->afreeitem;
|
|
WORD wBlock;
|
|
|
|
qFreeList->flh.wNumBlocks = wNumBlocks;
|
|
qFreeList->flh.wMaxBlocks = wMaxBlocks;
|
|
qFreeList->flh.dwLostBytes = dwLostBytes;
|
|
|
|
for (wBlock=0;wBlock<wNumBlocks;wBlock++,qCurrent++)
|
|
{
|
|
qCurrent->foStart.dwOffset = SWAPLONG(qCurrent->foStart.dwOffset);
|
|
qCurrent->foStart.dwHigh = SWAPLONG(qCurrent->foStart.dwHigh);
|
|
qCurrent->foBlock.dwOffset = SWAPLONG(qCurrent->foBlock.dwOffset);
|
|
qCurrent->foBlock.dwHigh = SWAPLONG(qCurrent->foBlock.dwHigh);
|
|
}
|
|
}
|
|
#endif // _BIG_E
|
|
|
|
_GLOBALUNLOCK(hFreeList);
|
|
|
|
return hFreeList;
|
|
|
|
exit1:
|
|
_GLOBALFREE(hFreeList);
|
|
exit0:
|
|
return NULL;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func HFREELIST PASCAL FAR | FreeListRealloc |
|
|
* Realloc a freelist structure. Memory image is
|
|
* always in Intel byte ordering format.
|
|
*
|
|
* @parm HFREELIST | hOldFreeList |
|
|
* Header to the old FreeList.
|
|
*
|
|
* @parm WORD | wMaxBlocks |
|
|
* New number of blocks.
|
|
*
|
|
* @parm PHRESULT | phr |
|
|
* Error code return valid if return value is NULL
|
|
*
|
|
* @rdesc Returns handle to a FREELIST, otherwise NULL if error.
|
|
*
|
|
***************************************************************************/
|
|
|
|
PUBLIC HFREELIST PASCAL FAR EXPORT_API FreeListRealloc( HFREELIST hOldFreeList, WORD wNewMaxBlocks, PHRESULT phr )
|
|
{
|
|
QFREELIST qFreeListMem;
|
|
QFREELIST qFreeList;
|
|
HFREELIST hFreeList = NULL;
|
|
WORD wMaxBlocks;
|
|
|
|
if (!hOldFreeList)
|
|
{
|
|
SetErrCode(phr, E_INVALIDARG);
|
|
return NULL;
|
|
}
|
|
|
|
if (!wNewMaxBlocks)
|
|
{
|
|
SetErrCode(phr, E_INVALIDARG);
|
|
return NULL;
|
|
}
|
|
|
|
if (!(qFreeListMem=_GLOBALLOCK(hOldFreeList)))
|
|
{
|
|
SetErrCode(phr,E_OUTOFMEMORY);
|
|
goto exit00;
|
|
}
|
|
|
|
wMaxBlocks = qFreeListMem->flh.wMaxBlocks;
|
|
|
|
if (! wMaxBlocks )
|
|
{
|
|
SetErrCode(phr, E_ASSERT);
|
|
goto exit0;
|
|
}
|
|
|
|
// Allocating new Freelist
|
|
if (!(hFreeList=_GLOBALALLOC(DLLGMEM_ZEROINIT,
|
|
sizeof(FREEITEM)*wNewMaxBlocks+sizeof(FREELISTHDR))))
|
|
{
|
|
SetErrCode(phr,E_OUTOFMEMORY);
|
|
goto exit0;
|
|
}
|
|
|
|
if (!(qFreeList=_GLOBALLOCK(hFreeList)))
|
|
{
|
|
SetErrCode(phr,E_OUTOFMEMORY);
|
|
goto exit1;
|
|
}
|
|
|
|
// Copying old one on new...
|
|
QVCOPY( qFreeList, qFreeListMem, sizeof(FREELISTHDR) + wMaxBlocks * sizeof(FREEITEM));
|
|
|
|
// ...Except the number of wMaxBlocks!
|
|
qFreeList->flh.wMaxBlocks = wNewMaxBlocks;
|
|
|
|
_GLOBALUNLOCK(hFreeList);
|
|
_GLOBALUNLOCK(hOldFreeList);
|
|
FreeListDestroy(hOldFreeList); // Bye bye, old one!
|
|
|
|
return hFreeList;
|
|
|
|
exit1:
|
|
_GLOBALFREE(hFreeList);
|
|
exit0:
|
|
_GLOBALUNLOCK(hOldFreeList);
|
|
exit00:
|
|
return NULL;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func LONG PASCAL FAR | FreeListBlockUsed |
|
|
* Returns the number of blocks used by the free list.
|
|
*
|
|
* @parm HFREELIST | hFreeList |
|
|
* List to get size of
|
|
*
|
|
* @parm PHRESULT | phr |
|
|
* Error return valid if return value is zero.
|
|
*
|
|
* @rdesc Number of bytes used by free list
|
|
*
|
|
***************************************************************************/
|
|
|
|
PUBLIC LONG PASCAL FAR EXPORT_API FreeListBlockUsed( HFREELIST hFreeList, PHRESULT phr )
|
|
{
|
|
QFREELIST qFreeList;
|
|
LONG lcbSize=0L;
|
|
WORD wNumBlocks=0L;
|
|
|
|
if (!hFreeList)
|
|
{
|
|
SetErrCode(phr, E_INVALIDARG);
|
|
return 0L;
|
|
}
|
|
|
|
if (!(qFreeList = _GLOBALLOCK(hFreeList)))
|
|
{ SetErrCode(phr,E_OUTOFMEMORY);
|
|
goto exit0;
|
|
}
|
|
|
|
lcbSize=qFreeList->flh.wMaxBlocks;
|
|
|
|
_GLOBALUNLOCK(hFreeList);
|
|
|
|
exit0:
|
|
return lcbSize;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func HRESULT PASCAL FAR | FreeListGetMem |
|
|
* Fill memory at lpvMem with freelist data. Call FreeListSize first
|
|
* to make sure the memory is large enough. Memory image will always
|
|
* be in Intel byte ordering format.
|
|
*
|
|
* @parm HFREELIST | hFreeList |
|
|
* List to retrieve
|
|
*
|
|
* @parm LPVOID | lpvMem |
|
|
* Pointer to memory to contain free list data
|
|
*
|
|
* @rdesc S_OK or other error
|
|
*
|
|
***************************************************************************/
|
|
|
|
PUBLIC HRESULT PASCAL FAR EXPORT_API FreeListGetMem( HFREELIST hFreeList, LPVOID lpvMem )
|
|
{
|
|
QFREELIST qFreeListMem;
|
|
QFREELIST qFreeList;
|
|
WORD wMaxBlocks;
|
|
WORD wNumBlocks;
|
|
DWORD dwLostBytes;
|
|
HRESULT rc = S_OK;
|
|
|
|
if ((!lpvMem) || (!hFreeList))
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
qFreeListMem = (QFREELIST)lpvMem;
|
|
|
|
if (!(qFreeList = _GLOBALLOCK(hFreeList)))
|
|
{ rc=E_OUTOFMEMORY;
|
|
goto exit0;
|
|
}
|
|
|
|
wMaxBlocks=qFreeList->flh.wMaxBlocks;
|
|
wNumBlocks=qFreeList->flh.wNumBlocks;
|
|
dwLostBytes=qFreeList->flh.dwLostBytes;
|
|
|
|
QVCOPY( qFreeListMem, qFreeList, sizeof(FREELISTHDR) + wMaxBlocks * sizeof(FREEITEM));
|
|
|
|
#ifdef _BIG_E
|
|
{
|
|
QFREEITEM qCurrent = qFreeListMem->afreeitem;
|
|
WORD wBlock;
|
|
|
|
qFreeListMem->flh.wNumBlocks = SWAPWORD( qFreeList->flh.wNumBlocks );
|
|
qFreeListMem->flh.wMaxBlocks = SWAPWORD( qFreeList->flh.wMaxBlocks );
|
|
qFreeListMem->flh.dwLostBytes = SWAPLONG( qFreeList->flh.dwLostBytes );
|
|
|
|
|
|
for (wBlock=0;wBlock<wNumBlocks;wBlock++,qCurrent++)
|
|
{
|
|
qCurrent->foStart.dwOffset = SWAPLONG(qCurrent->foStart.dwOffset);
|
|
qCurrent->foStart.dwHigh = SWAPLONG(qCurrent->foStart.dwHigh);
|
|
qCurrent->foBlock.dwOffset = SWAPLONG(qCurrent->foBlock.dwOffset);
|
|
qCurrent->foBlock.dwHigh = SWAPLONG(qCurrent->foBlock.dwHigh);
|
|
}
|
|
}
|
|
#endif // _BIG_E
|
|
|
|
_GLOBALUNLOCK(hFreeList);
|
|
|
|
exit0:
|
|
return rc;
|
|
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func HRESULT PASCAL FAR | FreeListDestroy |
|
|
* Remove all memory associated with the free list
|
|
*
|
|
* @parm HFREELIST | hFreeList |
|
|
* List to destroy
|
|
*
|
|
* @rdesc S_OK or E_INVALIDARG
|
|
*
|
|
* @comm
|
|
* The handle <p hFreeList> is no longer valid after this call.
|
|
*
|
|
***************************************************************************/
|
|
|
|
PUBLIC HRESULT PASCAL FAR EXPORT_API FreeListDestroy( HFREELIST hFreeList )
|
|
{
|
|
if (!hFreeList)
|
|
return E_INVALIDARG;
|
|
|
|
_GLOBALFREE(hFreeList);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func LONG PASCAL FAR | FreeListSize |
|
|
* Returns the number of bytes used by the free list.
|
|
*
|
|
* @parm HFREELIST | hFreeList |
|
|
* List to get size of
|
|
*
|
|
* @parm PHRESULT | phr |
|
|
* Error return valid if return value is zero.
|
|
*
|
|
* @rdesc Number of bytes used by free list
|
|
*
|
|
***************************************************************************/
|
|
|
|
PUBLIC LONG PASCAL FAR EXPORT_API FreeListSize( HFREELIST hFreeList, PHRESULT phr )
|
|
{
|
|
QFREELIST qFreeList;
|
|
LONG lcbSize=0L;
|
|
WORD wNumBlocks=0L;
|
|
|
|
if (!hFreeList)
|
|
{
|
|
SetErrCode(phr, E_INVALIDARG);
|
|
return 0L;
|
|
}
|
|
|
|
if (!(qFreeList = _GLOBALLOCK(hFreeList)))
|
|
{ SetErrCode(phr,E_OUTOFMEMORY);
|
|
goto exit0;
|
|
}
|
|
|
|
lcbSize=sizeof(FREELISTHDR)+qFreeList->flh.wMaxBlocks*sizeof(FREEITEM);
|
|
|
|
_GLOBALUNLOCK(hFreeList);
|
|
|
|
exit0:
|
|
return lcbSize;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func LONG PASCAL FAR | FreeListSize |
|
|
* Returns the number of bytes used by the memory image of a free list.
|
|
*
|
|
* @parm LPVOID | lpvMem |
|
|
* Memory image of list to get size of
|
|
*
|
|
* @parm PHRESULT | phr |
|
|
* Error return valid if return value is zero.
|
|
*
|
|
* @rdesc Number of bytes used in free list image
|
|
*
|
|
***************************************************************************/
|
|
|
|
PUBLIC LONG PASCAL FAR EXPORT_API FreeListSizeFromMem( LPVOID lpvMem, PHRESULT phr )
|
|
{
|
|
QFREELIST qFreeList;
|
|
LONG lcbSize=0L;
|
|
WORD wNumBlocks=0L;
|
|
|
|
if (!lpvMem)
|
|
{
|
|
SetErrCode(phr,E_INVALIDARG);
|
|
return 0L;
|
|
}
|
|
|
|
qFreeList = (QFREELIST)lpvMem;
|
|
|
|
wNumBlocks = qFreeList->flh.wNumBlocks;
|
|
lcbSize=sizeof(FREELISTHDR)+wNumBlocks*sizeof(FREEITEM);
|
|
|
|
return lcbSize;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func HRESULT FAR | FreeListAdd |
|
|
* Add a block to the free list. The free list is maintained in order
|
|
* by block starting address. Blocks are merged if adjacent.
|
|
*
|
|
* @parm HFREELIST | hFreeList |
|
|
* List to add entry to
|
|
*
|
|
* @parm FILEOFFSET | foStart |
|
|
* Starting address of free block to add
|
|
*
|
|
* @parm FILEOFFSET | foBlock |
|
|
* Size of block to add
|
|
*
|
|
* @rdesc S_OK, E_OUTOFMEMORY or ?
|
|
*
|
|
* @comm If all entries in the free list are filled, then the block
|
|
* with the smallest size is thrown out. This function is the heart
|
|
* of the FreeList object.
|
|
*
|
|
***************************************************************************/
|
|
|
|
PUBLIC HRESULT FAR EXPORT_API FreeListAdd(HFREELIST hFreeList, FILEOFFSET foStart, FILEOFFSET foBlock)
|
|
{
|
|
QFREELIST qFreeList;
|
|
WORD wNumBlocks;
|
|
HRESULT rc = S_OK;
|
|
QFREEITEM qPrevious = NULL;
|
|
QFREEITEM qCurrent;
|
|
BOOL bInserted = FALSE;
|
|
BOOL bFullList = FALSE;
|
|
short iLo, iHi, iMid, iSpan, iFound;
|
|
|
|
if (!hFreeList)
|
|
return E_INVALIDARG;
|
|
|
|
if (!(qFreeList = _GLOBALLOCK(hFreeList)))
|
|
return E_OUTOFMEMORY;
|
|
|
|
wNumBlocks=qFreeList->flh.wNumBlocks;
|
|
|
|
bFullList=(wNumBlocks==qFreeList->flh.wMaxBlocks);
|
|
|
|
if (wNumBlocks)
|
|
{
|
|
iLo=0;
|
|
iHi=wNumBlocks-1;
|
|
iSpan=iHi-iLo+1;
|
|
iFound=-1;
|
|
|
|
while (iSpan>0)
|
|
{
|
|
iMid=(iLo+iHi)/2;
|
|
//if (lifStart > qFreeList->afreeitem[iMid].lifStart)
|
|
if (FoCompare(foStart,qFreeList->afreeitem[iMid].foStart)>0)
|
|
{
|
|
if (iSpan==1)
|
|
{
|
|
iFound=iLo;
|
|
break;
|
|
}
|
|
iLo=min(iHi,iMid+1);
|
|
}
|
|
else
|
|
{ if (iSpan==1)
|
|
{
|
|
iFound=iLo-1;
|
|
break;
|
|
}
|
|
iHi=max(iLo,iMid-1);
|
|
}
|
|
|
|
iSpan=iHi-iLo+1;
|
|
}
|
|
|
|
// Number of blocks _after_ current block
|
|
wNumBlocks=qFreeList->flh.wNumBlocks-iFound-1;
|
|
|
|
// wFound == -1, insert at beginning,
|
|
// else insert _after_ wFound
|
|
qCurrent=qFreeList->afreeitem+(iFound+1);
|
|
if (iFound!=-1)
|
|
qPrevious=qCurrent-1;
|
|
|
|
if ((!qPrevious) ||
|
|
(!FoEquals(FoAddFo(qPrevious->foStart,qPrevious->foBlock),foStart)))
|
|
{ // Cannot merge with previous
|
|
//if ((wNumBlocks) && (qCurrent->foStart!=foStart+lcbBlock))
|
|
if ((!wNumBlocks) || (!FoEquals(qCurrent->foStart,FoAddFo(foStart,foBlock))))
|
|
{ // Cannot merge with next, insert new item
|
|
|
|
if (bFullList)
|
|
{ // Remove smallest item
|
|
FILEOFFSET foSmallest = foMax;
|
|
WORD wSmallestBlock = (WORD)-1;
|
|
QFREEITEM qTemp = qFreeList->afreeitem;
|
|
WORD wBlockTemp = 0;
|
|
|
|
// First we must find the smallest block
|
|
for (wBlockTemp=0;wBlockTemp < qFreeList->flh.wNumBlocks;wBlockTemp++)
|
|
{
|
|
if (FoCompare(qTemp->foBlock,foSmallest)<0)
|
|
{
|
|
foSmallest=qTemp->foBlock;
|
|
wSmallestBlock=wBlockTemp;
|
|
}
|
|
qTemp++;
|
|
}
|
|
|
|
// If our new block is smaller than the smallest, skip adding it in at all
|
|
if (FoCompare(foBlock,foSmallest)<=0)
|
|
{
|
|
goto exit1;
|
|
}
|
|
|
|
qFreeList->flh.dwLostBytes+=foSmallest.dwOffset;
|
|
|
|
// Remove smallest block, leaving hole at end of list
|
|
if (wSmallestBlock!=qFreeList->flh.wMaxBlocks-1)
|
|
{
|
|
QVCOPY(qFreeList->afreeitem+wSmallestBlock,
|
|
qFreeList->afreeitem+wSmallestBlock+1,
|
|
sizeof(FREEITEM)*(qFreeList->flh.wMaxBlocks-wSmallestBlock-1));
|
|
}
|
|
qFreeList->flh.wNumBlocks--;
|
|
wNumBlocks--;
|
|
// If the block found is before current, current must slide back one
|
|
if ((int)wSmallestBlock <= iFound)
|
|
{ qCurrent=qPrevious;
|
|
wNumBlocks++;
|
|
}
|
|
}
|
|
|
|
// Insert Item
|
|
if (wNumBlocks)
|
|
QVCOPY(qCurrent+1, qCurrent, sizeof(FREEITEM)*wNumBlocks);
|
|
|
|
qCurrent->foStart=foStart;
|
|
qCurrent->foBlock=foBlock;
|
|
qFreeList->flh.wNumBlocks++;
|
|
}
|
|
else
|
|
{ // Merge with next
|
|
qCurrent->foStart=foStart;
|
|
qCurrent->foBlock=FoAddFo(qCurrent->foBlock,foBlock);
|
|
}
|
|
}
|
|
else
|
|
{ // Merge with previous
|
|
qPrevious->foBlock=FoAddFo(qPrevious->foBlock,foBlock);
|
|
|
|
if (FoEquals(FoAddFo(qPrevious->foStart,qPrevious->foBlock),qCurrent->foStart))
|
|
{
|
|
// it fills a hole, merge with next
|
|
qPrevious->foBlock=FoAddFo(qPrevious->foBlock,qCurrent->foBlock);
|
|
|
|
// Scoot all next blocks back by one if any
|
|
if (wNumBlocks)
|
|
{
|
|
QVCOPY(qCurrent, qCurrent+1, sizeof(FREEITEM)*wNumBlocks);
|
|
qFreeList->flh.wNumBlocks--;
|
|
// wNumBlocks--; // not really needed, we break out
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else // first one
|
|
{
|
|
qCurrent=qFreeList->afreeitem;
|
|
qCurrent->foStart=foStart;
|
|
qCurrent->foBlock=foBlock;
|
|
qFreeList->flh.wNumBlocks++;
|
|
}
|
|
|
|
exit1:
|
|
_GLOBALUNLOCK(hFreeList);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func FILEOFFSET PASCAL FAR | FreeListGetBestFit |
|
|
* Gives the location of the block with best fit, and removes that area
|
|
* from the free list.
|
|
*
|
|
* @parm HFREELIST | hFreeList |
|
|
* Free List to pull block from
|
|
*
|
|
* @parm FILEOFFSET | foBlockDesired |
|
|
* Size of block to retrieve
|
|
*
|
|
* @parm PHRESULT | phr |
|
|
* Error return valid if return value is foNil
|
|
*
|
|
* @rdesc Location of block, or foNil if error
|
|
*
|
|
***************************************************************************/
|
|
|
|
PUBLIC FILEOFFSET PASCAL FAR EXPORT_API FreeListGetBestFit(HFREELIST hFreeList, FILEOFFSET foBlockDesired, PHRESULT phr)
|
|
{
|
|
QFREELIST qFreeList;
|
|
FILEOFFSET foStart=foNil;
|
|
WORD wNumBlocks;
|
|
WORD wBlock;
|
|
WORD wBestBlock=(WORD)-1;
|
|
FILEOFFSET foMinLeftOver = foMax;
|
|
QFREEITEM qCurrent;
|
|
QFREEITEM qBestBlock;
|
|
|
|
if (!hFreeList)
|
|
{
|
|
SetErrCode(phr,E_INVALIDARG);
|
|
goto exit0;
|
|
}
|
|
|
|
if (!(qFreeList = _GLOBALLOCK(hFreeList)))
|
|
{ SetErrCode(phr,E_OUTOFMEMORY);
|
|
goto exit0;
|
|
}
|
|
|
|
wNumBlocks=qFreeList->flh.wNumBlocks;
|
|
qCurrent=qFreeList->afreeitem;
|
|
|
|
for (wBlock=0;wBlock<wNumBlocks;wBlock++)
|
|
{
|
|
FILEOFFSET foDiff = FoSubFo(qCurrent->foBlock,foBlockDesired);
|
|
if ((FoCompare(foDiff,foNil)>=0) && (FoCompare(foDiff,foMinLeftOver)<0))
|
|
{
|
|
foMinLeftOver=foDiff; // if zero, break
|
|
wBestBlock=wBlock;
|
|
qBestBlock=qCurrent;
|
|
}
|
|
qCurrent++;
|
|
}
|
|
|
|
if (wBestBlock!=(WORD)-1)
|
|
{
|
|
foStart=qBestBlock->foStart;
|
|
qBestBlock->foStart=FoAddFo(qBestBlock->foStart,foBlockDesired);
|
|
qBestBlock->foBlock=FoSubFo(qBestBlock->foBlock,foBlockDesired);
|
|
if (phr)
|
|
*phr=S_OK;
|
|
|
|
if (FoIsNil(qBestBlock->foBlock))
|
|
{
|
|
WORD wBlocksFollowing = wNumBlocks-wBestBlock-1;
|
|
// Remove block from list
|
|
if (wBlocksFollowing)
|
|
QVCOPY(qBestBlock,qBestBlock+1,sizeof(FREEITEM)*wBlocksFollowing);
|
|
|
|
qFreeList->flh.wNumBlocks--;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (phr)
|
|
*phr =E_NOTEXIST; // Normal OK condition, do not use SetErrCode!
|
|
}
|
|
|
|
_GLOBALUNLOCK(hFreeList);
|
|
|
|
exit0:
|
|
return foStart;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func FILEOFFSET PASCAL FAR | FreeListGetBlockAt |
|
|
* If a free block exists at foStart, the size of the block is returned
|
|
* and is taken out of the free list.
|
|
*
|
|
* @parm HFREELIST | hFreeList |
|
|
* Free List to pull block from
|
|
*
|
|
* @parm FILEOFFSET | foStart |
|
|
* Starting address of block
|
|
*
|
|
* @parm PHRESULT | phr |
|
|
* Error return valid if return value is foNil
|
|
*
|
|
* @rdesc Size of block pulled, or foNil if error occurred (E_NOTEXIST mostly)
|
|
*
|
|
* @comm
|
|
* Block from foStart of returned length removed from list.
|
|
*
|
|
***************************************************************************/
|
|
|
|
PUBLIC FILEOFFSET PASCAL FAR EXPORT_API FreeListGetBlockAt(HFREELIST hFreeList, FILEOFFSET foStart, PHRESULT phr )
|
|
{
|
|
QFREELIST qFreeList;
|
|
QFREEITEM qCurrent;
|
|
FILEOFFSET foBlock = foNil;
|
|
WORD wNumBlocks;
|
|
short iLo, iHi, iMid, iSpan, iFound;
|
|
|
|
if (!hFreeList)
|
|
{
|
|
SetErrCode(phr,E_INVALIDARG);
|
|
goto exit0;
|
|
}
|
|
|
|
if (!(qFreeList = _GLOBALLOCK(hFreeList)))
|
|
{
|
|
SetErrCode(phr,E_OUTOFMEMORY);
|
|
goto exit0;
|
|
}
|
|
|
|
wNumBlocks=qFreeList->flh.wNumBlocks;
|
|
|
|
if (wNumBlocks)
|
|
{
|
|
iLo=0;
|
|
iHi=wNumBlocks-1;
|
|
iSpan=iHi-iLo+1;
|
|
iFound=-1;
|
|
|
|
while (iSpan>0)
|
|
{
|
|
short iCompare;
|
|
iMid=(iLo+iHi)/2;
|
|
|
|
iCompare=FoCompare(foStart,qFreeList->afreeitem[iMid].foStart);
|
|
|
|
if (iCompare>0)
|
|
{
|
|
if (iSpan==1)
|
|
{
|
|
iFound=iLo;
|
|
break;
|
|
}
|
|
iLo=min(iHi,iMid+1);
|
|
}
|
|
else if (iCompare<0)
|
|
{ if (iSpan==1)
|
|
{
|
|
iFound=iLo-1;
|
|
break;
|
|
}
|
|
iHi=max(iLo,iMid-1);
|
|
}
|
|
else
|
|
{
|
|
iFound=iMid;
|
|
break;
|
|
|
|
}
|
|
|
|
iSpan=iHi-iLo+1;
|
|
}
|
|
|
|
if (iFound!=-1)
|
|
{
|
|
qCurrent=qFreeList->afreeitem+iFound;
|
|
|
|
assert(FoCompare(foStart,qCurrent->foStart)>=0);
|
|
|
|
if (FoCompare(foStart,FoAddFo(qCurrent->foStart,qCurrent->foBlock))<0)
|
|
{
|
|
// We found a block that can start here!
|
|
// Return length to end of block
|
|
foBlock=FoAddFo(FoSubFo(qCurrent->foStart,foStart),qCurrent->foBlock);
|
|
qCurrent->foBlock=FoSubFo(qCurrent->foBlock,foBlock);
|
|
|
|
// phr should already be set to this before function is called
|
|
// SetErrCode(phr,S_OK);
|
|
|
|
if (FoIsNil(qCurrent->foBlock))
|
|
{
|
|
// Grabbed entire block, so remove it from list
|
|
WORD wBlocksFollowing = wNumBlocks-iFound-1;
|
|
if (wBlocksFollowing)
|
|
QVCOPY(qCurrent,qCurrent+1,sizeof(FREEITEM)*wBlocksFollowing);
|
|
|
|
qFreeList->flh.wNumBlocks--;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetErrCode(phr,E_NOTEXIST);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetErrCode(phr,E_NOTEXIST);
|
|
}
|
|
}
|
|
|
|
_GLOBALUNLOCK(hFreeList);
|
|
|
|
exit0:
|
|
return foBlock;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func HRESULT PASCAL FAR | FreeListGetLastBlock |
|
|
* If any blocks are in the list, return the offset and size of the
|
|
* last block in the list.
|
|
*
|
|
* @parm HFREELIST | hFreeList |
|
|
* Free List to pull block from
|
|
*
|
|
* @parm FILEOFFSET * | pfoStart |
|
|
* Starting address of block
|
|
*
|
|
* @parm FILEOFFSET * | pfoSize |
|
|
* Size of last block
|
|
*
|
|
* @parm FILEOFFSET * | foEof |
|
|
* End of valid file that free list is representing. The last block must
|
|
* end at or past the end of file to be returned (otherwise it isn't really
|
|
* the last in the structure being represented, as there will be data after
|
|
* the last block...
|
|
*
|
|
* @rdesc S_OK if last block exists, else E_NOTEXIST
|
|
*
|
|
***************************************************************************/
|
|
|
|
PUBLIC HRESULT PASCAL FAR EXPORT_API FreeListGetLastBlock(HFREELIST hFreeList,
|
|
FILEOFFSET * pfoStart, FILEOFFSET * pfoSize, FILEOFFSET foEof)
|
|
{
|
|
QFREELIST qFreeList;
|
|
QFREEITEM qCurrent;
|
|
WORD wNumBlocks;
|
|
HRESULT errb;
|
|
|
|
errb=S_OK;
|
|
|
|
if (!hFreeList)
|
|
{
|
|
SetErrCode(&errb,E_INVALIDARG);
|
|
goto exit0;
|
|
}
|
|
|
|
if (!(qFreeList = _GLOBALLOCK(hFreeList)))
|
|
{
|
|
SetErrCode(&errb,E_OUTOFMEMORY);
|
|
goto exit0;
|
|
}
|
|
|
|
wNumBlocks=qFreeList->flh.wNumBlocks;
|
|
|
|
if (wNumBlocks)
|
|
{
|
|
qCurrent=qFreeList->afreeitem+(wNumBlocks-1);
|
|
|
|
if (FoCompare(FoAddFo(qCurrent->foStart,qCurrent->foBlock),foEof) >= 0)
|
|
{
|
|
*pfoStart=qCurrent->foStart;
|
|
*pfoSize=qCurrent->foBlock;
|
|
// Always Grabs entire block, so remove it from list
|
|
qFreeList->flh.wNumBlocks--;
|
|
}
|
|
else
|
|
SetErrCode(&errb,E_NOTEXIST);
|
|
}
|
|
else
|
|
SetErrCode(&errb,E_NOTEXIST);
|
|
|
|
_GLOBALUNLOCK(hFreeList);
|
|
|
|
exit0:
|
|
return errb;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func HRESULT PASCAL FAR | FreeListGetStats |
|
|
* Get stats like number of bytes in free list, and # of bytes lost
|
|
*
|
|
* @parm HFREELIST | hFreeList |
|
|
* Free List to query
|
|
*
|
|
* @parm LPFREELISTSTATS | lpStats |
|
|
* Pointer to status struct:
|
|
*
|
|
* @struct FREELISTSTATS | Structure for statistics
|
|
* @field DWORD | dwBytesInFreeList | Number of bytes being kept track of by
|
|
* freelist.
|
|
* @field DWORD | dwBytesLostForever | Number of bytes free list has lost track of
|
|
* @field DWORD | dwSmallestBlock | Size of smallest block being kept track of.
|
|
* @field DWORD | dwLargestBlock | Size of largest block being kept track of.
|
|
* @field WORD | wNumBlocks | Number of blocks being kept track of.
|
|
*
|
|
* @rdesc S_OK if OK, else an error
|
|
*
|
|
***************************************************************************/
|
|
|
|
PUBLIC HRESULT PASCAL FAR EXPORT_API FreeListGetStats(HFREELIST hFreeList, LPFREELISTSTATS lpStats)
|
|
{
|
|
QFREELIST qFreeList;
|
|
WORD wNumBlocks;
|
|
|
|
if (!hFreeList)
|
|
return E_INVALIDARG;
|
|
|
|
if (!(qFreeList = _GLOBALLOCK(hFreeList)))
|
|
return E_OUTOFMEMORY;
|
|
|
|
lpStats->wNumBlocks=wNumBlocks=qFreeList->flh.wNumBlocks;
|
|
lpStats->dwBytesLostForever=qFreeList->flh.dwLostBytes;
|
|
lpStats->dwBytesInFreeList=0L;
|
|
lpStats->dwSmallestBlock=0L;
|
|
lpStats->dwLargestBlock=0L;
|
|
|
|
if (wNumBlocks)
|
|
{
|
|
FILEOFFSET foSmallest = foMax;
|
|
FILEOFFSET foLargest = foMin;
|
|
QFREEITEM qTemp = qFreeList->afreeitem;
|
|
WORD wBlockTemp = 0;
|
|
|
|
// First we must find the smallest block
|
|
for (wBlockTemp=0;wBlockTemp < wNumBlocks;wBlockTemp++)
|
|
{
|
|
lpStats->dwBytesInFreeList+=qTemp->foBlock.dwOffset;
|
|
if (FoCompare(qTemp->foBlock,foSmallest)<0)
|
|
foSmallest=qTemp->foBlock;
|
|
if (FoCompare(qTemp->foBlock, foLargest)>0)
|
|
foLargest=qTemp->foBlock;
|
|
qTemp++;
|
|
}
|
|
lpStats->dwSmallestBlock=(DWORD)foSmallest.dwOffset;
|
|
lpStats->dwLargestBlock=(DWORD)foLargest.dwOffset;
|
|
}
|
|
|
|
_GLOBALUNLOCK(hFreeList);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* Cute little memory block manager incorporating the free list!
|
|
* 65200 bytes are allocated for use by anyone using NewMemory() and
|
|
* DisposeMemory(). Mainly this is used by the FM construct for filenames,
|
|
* avoiding the overhead of GlobalLock and GlobalAlloc. Of course, this
|
|
* could be debatable depending on how fast the Free List works. The only
|
|
* linear search in the Free List is finding the best fit block. Anyway,
|
|
* feel free to replace the scheme below if something more ingenious comes
|
|
* to mind. This is still faster than using GlobalAllocks at least in the
|
|
* case where FM.C uses this routine.
|
|
*
|
|
***************************************************************************/
|
|
|
|
#define MEMORYBLOCK_FREEITEMS 1024
|
|
|
|
typedef struct _memory_block
|
|
{
|
|
HFREELIST hfl;
|
|
LPSTR lpEom;
|
|
LONG lcbFree;
|
|
char lpMemory[1];
|
|
} MEMORY_BLOCK;
|
|
|
|
typedef MEMORY_BLOCK FAR * LPMB;
|
|
|
|
LONG PASCAL NEAR MemoryBlockStatus(LPMB lpmb)
|
|
{
|
|
assert(lpmb);
|
|
return lpmb->lcbFree;
|
|
}
|
|
|
|
LPMB PASCAL NEAR MemoryBlockInit( LONG lcbSize )
|
|
{
|
|
HANDLE hmb;
|
|
LPMB lpmb;
|
|
HRESULT errb;
|
|
|
|
if (!(hmb=_GLOBALALLOC(DLLGMEM_ZEROINIT,sizeof(MEMORY_BLOCK)+lcbSize)))
|
|
return NULL;
|
|
if (!(lpmb=(LPMB)_GLOBALLOCK(hmb)))
|
|
{
|
|
_GLOBALFREE(hmb);
|
|
return NULL;
|
|
}
|
|
|
|
lpmb->lpEom=lpmb->lpMemory;
|
|
lpmb->lcbFree=lcbSize;
|
|
if (!(lpmb->hfl=FreeListInit(MEMORYBLOCK_FREEITEMS,&errb)))
|
|
{
|
|
_GLOBALUNLOCK(hmb);
|
|
_GLOBALFREE(hmb);
|
|
return NULL;
|
|
}
|
|
return lpmb;
|
|
}
|
|
|
|
HRESULT PASCAL NEAR MemoryBlockDestroy( LPMB lpmb)
|
|
{
|
|
assert(lpmb);
|
|
|
|
FreeListDestroy(lpmb->hfl);
|
|
_GLOBALUNLOCK(GlobalHandle(lpmb));
|
|
_GLOBALFREE(GlobalHandle(lpmb));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
LPSTR PASCAL NEAR MemoryBlockNew(LPMB lpmb, WORD wcbSize)
|
|
{
|
|
HRESULT errb;
|
|
LPSTR lpMemory = NULL;
|
|
FILEOFFSET foFound;
|
|
FILEOFFSET foSize;
|
|
WORD FAR * lpw;
|
|
|
|
foSize.dwOffset=(LONG)wcbSize+sizeof(WORD);
|
|
foSize.dwHigh=0;
|
|
|
|
errb=S_OK;
|
|
foFound=FreeListGetBestFit(lpmb->hfl,foSize,&errb);
|
|
if (errb==S_OK)
|
|
{
|
|
lpw=(WORD FAR *)(lpMemory=lpmb->lpMemory+foFound.dwOffset);
|
|
lpMemory+=sizeof(WORD);
|
|
*(LPUW)lpw = wcbSize;
|
|
}
|
|
else
|
|
{
|
|
if ((LONG)wcbSize+(LONG)sizeof(WORD)>(LONG)lpmb->lcbFree)
|
|
return NULL;
|
|
lpw=(WORD FAR *)(lpMemory=lpmb->lpEom);
|
|
lpMemory+=sizeof(WORD);
|
|
lpmb->lpEom+=(LONG)wcbSize+sizeof(WORD);
|
|
lpmb->lcbFree-=(LONG)wcbSize+sizeof(WORD);
|
|
*(LPUW)lpw = wcbSize;
|
|
}
|
|
return lpMemory;
|
|
}
|
|
|
|
void PASCAL NEAR MemoryBlockDispose(LPMB lpmb, LPSTR lpMemory)
|
|
{
|
|
WORD FAR *lpw;
|
|
FILEOFFSET fo,foLen;
|
|
assert(lpMemory);
|
|
lpw=(WORD FAR *)(lpMemory-sizeof(WORD));
|
|
|
|
fo.dwOffset=(DWORD)(lpMemory-sizeof(WORD)-lpmb->lpMemory);
|
|
fo.dwHigh=0;
|
|
#ifdef _RISC_PATCH //Misalignment problems
|
|
foLen.dwOffset=(DWORD)(*(LPUW)lpw+2); // Size of lpw, which is hardcoded to two bytes.
|
|
#else
|
|
foLen.dwOffset=(DWORD)*lpw+sizeof(WORD);
|
|
#endif
|
|
foLen.dwHigh=0;
|
|
|
|
FreeListAdd(lpmb->hfl, fo, foLen);
|
|
}
|
|
|
|
|
|
// Global common memory block implementation - for everyone to use!!!
|
|
|
|
LPMB glpmb=NULL;
|
|
LONG gctMemoryBlocks=0;
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc PRIVATE
|
|
*
|
|
* @func LPSTR PASCAL FAR | NewMemory |
|
|
* Allocates memory from an always locked memory block
|
|
*
|
|
* @parm WORD | wcbSize |
|
|
* Amount to allocate (total allocations must to surpass 65200 bytes)
|
|
*
|
|
* @rdesc Pointer to an allocated and locked memory block, or NULL if error
|
|
*
|
|
* @comm The first time <f NewMemory> is called, a block of 65200 byes
|
|
* is allocated, and this block is only released when the last corresponding
|
|
* <f DisposeMemory> call is made. Currently this API is used exclusively
|
|
* by the FM routines for allocating strings for temporary usage.
|
|
*
|
|
***************************************************************************/
|
|
|
|
PUBLIC LPSTR PASCAL FAR EXPORT_API NewMemory( WORD wcbSize )
|
|
{
|
|
LPSTR lpNew=NULL;
|
|
|
|
if (wcbSize)
|
|
{
|
|
if (!glpmb)
|
|
{
|
|
if (!(glpmb=MemoryBlockInit(65200L)))
|
|
return NULL;
|
|
}
|
|
|
|
if (lpNew=MemoryBlockNew(glpmb, wcbSize))
|
|
gctMemoryBlocks++;
|
|
}
|
|
return lpNew;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc PRIVATE
|
|
*
|
|
* @func void PASCAL FAR | DisposeMemory |
|
|
* Frees the memory previous allocated with NewMemory
|
|
*
|
|
* @parm LPSTR | szMemory |
|
|
* Start of memory block to free (must have been returned
|
|
* from NewMemory)
|
|
*
|
|
***************************************************************************/
|
|
|
|
PUBLIC void PASCAL FAR EXPORT_API DisposeMemory( LPSTR lpMemory)
|
|
{
|
|
assert(lpMemory);
|
|
MemoryBlockDispose(glpmb, lpMemory);
|
|
if (!(--gctMemoryBlocks))
|
|
{
|
|
MemoryBlockDestroy(glpmb);
|
|
glpmb=NULL;
|
|
}
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* @doc INTERNAL API
|
|
*
|
|
* @func WORD PASCAL FAR | StatusOfMemory |
|
|
* Returns the number of bytes left that can be allocated
|
|
*
|
|
* @rdesc Number of bytes left in global memory block
|
|
*
|
|
***************************************************************************/
|
|
|
|
PUBLIC WORD PASCAL FAR EXPORT_API StatusOfMemory( void )
|
|
{
|
|
if (glpmb)
|
|
return (WORD)MemoryBlockStatus(glpmb);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|