mirror of
https://github.com/microsoft/Microsoft-3D-Movie-Maker.git
synced 2024-11-22 02:12:33 +01:00
1119 lines
27 KiB
C++
1119 lines
27 KiB
C++
/* Copyright (c) Microsoft Corporation.
|
|
Licensed under the MIT License. */
|
|
|
|
/***************************************************************************
|
|
Author: ShonK
|
|
Project: Kauai
|
|
Reviewed:
|
|
Copyright (c) Microsoft Corporation
|
|
|
|
Chunky resource management.
|
|
|
|
WARNING: BACOs should only be released or fetched from the main
|
|
thread! CRFs are NOT thread safe! Alternatively, the BACO can be
|
|
detached from the CRF (in the main thread), then later released
|
|
in a different thread.
|
|
|
|
***************************************************************************/
|
|
#include "util.h"
|
|
ASSERTNAME
|
|
|
|
|
|
RTCLASS(BACO)
|
|
RTCLASS(GHQ)
|
|
RTCLASS(RCA)
|
|
RTCLASS(CRF)
|
|
RTCLASS(CRM)
|
|
RTCLASS(CABO)
|
|
|
|
|
|
/***************************************************************************
|
|
Constructor for base cacheable object.
|
|
***************************************************************************/
|
|
BACO::BACO(void)
|
|
{
|
|
AssertBaseThis(fobjAllocated);
|
|
_pcrf = pvNil;
|
|
_crep = crepToss;
|
|
_fAttached = fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Destructor.
|
|
***************************************************************************/
|
|
BACO::~BACO(void)
|
|
{
|
|
AssertBaseThis(fobjAllocated);
|
|
Assert(!_fAttached, "still attached");
|
|
ReleasePpo(&_pcrf);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Write the BACO to a FLO - just make the FLO a BLCK and write to
|
|
the block.
|
|
***************************************************************************/
|
|
bool BACO::FWriteFlo(PFLO pflo)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pflo, 0);
|
|
BLCK blck(pflo);
|
|
return FWrite(&blck);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Placeholder function for BACO generic writer.
|
|
***************************************************************************/
|
|
bool BACO::FWrite(PBLCK pblck)
|
|
{
|
|
AssertThis(0);
|
|
RawRtn(); // Derived class should be defining this
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Placeholder function for BACO generic cb-getter.
|
|
***************************************************************************/
|
|
long BACO::CbOnFile(void)
|
|
{
|
|
AssertThis(0);
|
|
RawRtn(); // Derived class should be defining this
|
|
return 0;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Assert the validity of a BACO.
|
|
***************************************************************************/
|
|
void BACO::AssertValid(ulong grf)
|
|
{
|
|
BACO_PAR::AssertValid(fobjAllocated);
|
|
Assert(!_fAttached || pvNil != _pcrf, "attached baco has no crf");
|
|
AssertNilOrVarMem(_pcrf);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Mark memory for the BACO.
|
|
***************************************************************************/
|
|
void BACO::MarkMem(void)
|
|
{
|
|
AssertValid(0);
|
|
BACO_PAR::MarkMem();
|
|
if (!_fAttached)
|
|
MarkMemObj(_pcrf);
|
|
}
|
|
#endif //DEBUG
|
|
|
|
|
|
/***************************************************************************
|
|
Release a reference to the BACO. If the reference count goes to zero
|
|
and the BACO is not attached, it is deleted.
|
|
***************************************************************************/
|
|
void BACO::Release(void)
|
|
{
|
|
AssertThis(0);
|
|
if (_cactRef-- <= 0)
|
|
{
|
|
Bug("calling Release without an AddRef");
|
|
_cactRef = 0;
|
|
}
|
|
if (_cactRef == 0)
|
|
{
|
|
if (!_fAttached)
|
|
delete this;
|
|
else
|
|
{
|
|
AssertPo(_pcrf, 0);
|
|
_pcrf->BacoReleased(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Detach a BACO from its CRF.
|
|
***************************************************************************/
|
|
void BACO::Detach(void)
|
|
{
|
|
AssertThis(0);
|
|
if (_fAttached)
|
|
{
|
|
AssertPo(_pcrf, 0);
|
|
_pcrf->AddRef();
|
|
_fAttached = fFalse;
|
|
_pcrf->BacoDetached(this);
|
|
}
|
|
if (_cactRef <= 0)
|
|
delete this;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the crep for the BACO.
|
|
***************************************************************************/
|
|
void BACO::SetCrep(long crep)
|
|
{
|
|
AssertThis(0);
|
|
// An AddRef followed by Release is done so that BacoReleased() is
|
|
// called if this BACO's _cactRef is 0...if crep is crepToss, this
|
|
// detaches this BACO from the cache.
|
|
AddRef();
|
|
_crep = crep;
|
|
Release();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Constructor for CRF. Increments the open count on the CFL.
|
|
***************************************************************************/
|
|
CRF::CRF(PCFL pcfl, long cbMax)
|
|
{
|
|
AssertBaseThis(fobjAllocated);
|
|
AssertPo(pcfl, 0);
|
|
AssertIn(cbMax, 0, kcbMax);
|
|
|
|
pcfl->AddRef();
|
|
_pcfl = pcfl;
|
|
_cbMax = cbMax;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Destructor for the CRF. Decrements the open count on the CFL and frees
|
|
all the cached data.
|
|
***************************************************************************/
|
|
CRF::~CRF(void)
|
|
{
|
|
AssertBaseThis(fobjAllocated);
|
|
CRE cre;
|
|
|
|
_cactRef++; //so we don't get "deleted" while detaching the BACOs
|
|
if (pvNil != _pglcre)
|
|
{
|
|
while (_pglcre->IvMac() > 0)
|
|
{
|
|
_pglcre->Get(0, &cre);
|
|
cre.pbaco->AddRef(); //so it doesn't go away when being detached
|
|
cre.pbaco->Detach();
|
|
cre.pbaco->_pcrf = pvNil; //we're going away!
|
|
Debug( _cactRef--; )
|
|
cre.pbaco->Release();
|
|
}
|
|
ReleasePpo(&_pglcre);
|
|
}
|
|
Assert(_cactRef == 1, "someone still refers to this CRF");
|
|
ReleasePpo(&_pcfl);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Static method to create a new chunky resource file cache.
|
|
***************************************************************************/
|
|
PCRF CRF::PcrfNew(PCFL pcfl, long cbMax)
|
|
{
|
|
AssertPo(pcfl, 0);
|
|
AssertIn(cbMax, 0, kcbMax);
|
|
PCRF pcrf;
|
|
|
|
if (pvNil != (pcrf = NewObj CRF(pcfl, cbMax)) &&
|
|
pvNil == (pcrf->_pglcre = GL::PglNew(size(CRE), 5)))
|
|
{
|
|
ReleasePpo(&pcrf);
|
|
}
|
|
AssertNilOrPo(pcrf, 0);
|
|
return pcrf;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the size of the cache. This is most effecient when cbMax is 0
|
|
(all non-required BACOs are flushed) or is bigger than the current
|
|
cbMax.
|
|
***************************************************************************/
|
|
void CRF::SetCbMax(long cbMax)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(cbMax, 0, kcbMax);
|
|
|
|
if (0 == cbMax)
|
|
{
|
|
CRE cre;
|
|
long icre;
|
|
|
|
for (icre = _pglcre->IvMac(); icre-- > 0; )
|
|
{
|
|
_pglcre->Get(icre, &cre);
|
|
AssertPo(cre.pbaco, 0);
|
|
if (cre.pbaco->CactRef() == 0)
|
|
{
|
|
Assert(cre.pbaco->_fAttached, "BACO not attached!");
|
|
cre.pbaco->Detach();
|
|
|
|
// have to start over in case other BACOs got deleted or
|
|
// reference counts went to zero
|
|
icre = _pglcre->IvMac();
|
|
}
|
|
}
|
|
}
|
|
else if (_cbCur > cbMax)
|
|
_FPurgeCb(_cbCur - cbMax, klwMax);
|
|
|
|
_cbMax = cbMax;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Pre-fetch the object. Returns tYes if the chunk is successfully cached,
|
|
tNo if the chunk isn't in the CRF and tMaybe if there wasn't room
|
|
to cache the chunk.
|
|
***************************************************************************/
|
|
bool CRF::TLoad(CTG ctg, CNO cno, PFNRPO pfnrpo, RSC rsc, long crep)
|
|
{
|
|
AssertThis(0);
|
|
Assert(pvNil != pfnrpo, "bad pfnrpo");
|
|
Assert(crep > crepToss, "crep too small");
|
|
CRE cre;
|
|
long icre;
|
|
BLCK blck;
|
|
|
|
//see if this CRF contains this resource type
|
|
if (rscNil != rsc && !_pcfl->FFind(kctgRsc, rsc))
|
|
return tNo;
|
|
|
|
//see if it's in the cache
|
|
if (_FFindCre(ctg, cno, pfnrpo, &icre))
|
|
{
|
|
_pglcre->Get(icre, &cre);
|
|
cre.pbaco->SetCrep(LwMax(cre.pbaco->_crep, crep));
|
|
cre.cactRelease = _cactRelease++;
|
|
_pglcre->Put(icre, &cre);
|
|
return tYes;
|
|
}
|
|
|
|
//see if it's in the chunky file
|
|
if (!_pcfl->FFind(ctg, cno, &blck))
|
|
return tNo;
|
|
|
|
//get the approximate size of the object
|
|
if (!(*pfnrpo)(this, ctg, cno, &blck, pvNil, &cre.cb))
|
|
return tMaybe;
|
|
|
|
if (_cbCur + cre.cb > _cbMax)
|
|
{
|
|
if (!_FPurgeCb(_cbCur + cre.cb - _cbMax, crep - 1))
|
|
return tMaybe;
|
|
}
|
|
|
|
if (!(*pfnrpo)(this, ctg, cno, &blck, &cre.pbaco, &cre.cb))
|
|
return tMaybe;
|
|
|
|
AssertPo(cre.pbaco, 0);
|
|
AssertIn(cre.cb, 0, kcbMax);
|
|
|
|
if (_cbCur + cre.cb > _cbMax &&
|
|
!_FPurgeCb(_cbCur + cre.cb - _cbMax, crep - 1))
|
|
{
|
|
ReleasePpo(&cre.pbaco);
|
|
return tMaybe;
|
|
}
|
|
|
|
cre.pbaco->_pcrf = this;
|
|
cre.pbaco->_ctg = ctg;
|
|
cre.pbaco->_cno = cno;
|
|
cre.pbaco->_crep = crep;
|
|
|
|
AddRef(); //until the baco is attached it needs a reference count
|
|
cre.pbaco->_fAttached = fFalse;
|
|
cre.pfnrpo = pfnrpo;
|
|
cre.cactRelease = _cactRelease++;
|
|
|
|
//indexes may have changed, get the location to insert again
|
|
AssertDo(!_FFindCre(ctg, cno, pfnrpo, &icre), "how did this happen?");
|
|
|
|
if (!_pglcre->FInsert(icre, &cre))
|
|
{
|
|
//can't keep it loaded
|
|
ReleasePpo(&cre.pbaco);
|
|
return tMaybe;
|
|
}
|
|
|
|
_cbCur += cre.cb;
|
|
cre.pbaco->_fAttached = fTrue;
|
|
cre.pbaco->Release();
|
|
Release(); //baco successfully attached, so release its reference count
|
|
|
|
return tYes;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Make sure the object is loaded and increment its reference count. If
|
|
successful, must be balanced with a call to ReleasePpo.
|
|
***************************************************************************/
|
|
PBACO CRF::PbacoFetch(CTG ctg, CNO cno, PFNRPO pfnrpo, bool *pfError, RSC rsc)
|
|
{
|
|
AssertThis(0);
|
|
Assert(pvNil != pfnrpo, "bad pfnrpo");
|
|
AssertNilOrVarMem(pfError);
|
|
CRE cre;
|
|
long icre;
|
|
BLCK blck;
|
|
|
|
if (pvNil != pfError)
|
|
*pfError = fFalse;
|
|
|
|
//see if this CRF contains this resource type
|
|
if (rscNil != rsc && !_pcfl->FFind(kctgRsc, rsc))
|
|
return pvNil;
|
|
|
|
//see if it's in the cache
|
|
if (_FFindCre(ctg, cno, pfnrpo, &icre))
|
|
{
|
|
_pglcre->Get(icre, &cre);
|
|
AssertPo(cre.pbaco, 0);
|
|
cre.pbaco->AddRef();
|
|
return cre.pbaco;
|
|
}
|
|
|
|
//see if it's in the chunky file
|
|
if (!_pcfl->FFind(ctg, cno, &blck))
|
|
return pvNil;
|
|
|
|
//get the object and its size
|
|
if (!(*pfnrpo)(this, ctg, cno, &blck, &cre.pbaco, &cre.cb))
|
|
{
|
|
if (pvNil != pfError)
|
|
*pfError = fTrue;
|
|
PushErc(ercCrfCantLoad);
|
|
return pvNil;
|
|
}
|
|
|
|
AssertPo(cre.pbaco, 0);
|
|
AssertIn(cre.cb, 0, kcbMax);
|
|
|
|
cre.pbaco->_pcrf = this;
|
|
cre.pbaco->_ctg = ctg;
|
|
cre.pbaco->_cno = cno;
|
|
cre.pbaco->_crep = crepNormal;
|
|
|
|
AddRef();
|
|
cre.pbaco->_fAttached = fFalse;
|
|
cre.pfnrpo = pfnrpo;
|
|
|
|
//indexes may have changed, get the location to insert again
|
|
AssertDo(!_FFindCre(ctg, cno, pfnrpo, &icre), "how did this happen?");
|
|
|
|
if (!_pglcre->FInsert(icre, &cre))
|
|
{
|
|
//return the pbaco anyway. when it's released it will go away
|
|
if (pvNil != pfError)
|
|
*pfError = fTrue;
|
|
return cre.pbaco;
|
|
}
|
|
|
|
_cbCur += cre.cb;
|
|
cre.pbaco->_fAttached = fTrue;
|
|
Release();
|
|
|
|
if (_cbCur > _cbMax)
|
|
{
|
|
//purge some stuff
|
|
_FPurgeCb(_cbCur - _cbMax, klwMax);
|
|
}
|
|
|
|
return cre.pbaco;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
If the object is loaded, increment its reference count and return it.
|
|
If it's not already loaded, just return nil.
|
|
***************************************************************************/
|
|
PBACO CRF::PbacoFind(CTG ctg, CNO cno, PFNRPO pfnrpo, RSC rsc)
|
|
{
|
|
AssertThis(0);
|
|
Assert(pvNil != pfnrpo, "bad pfnrpo");
|
|
|
|
CRE cre;
|
|
long icre;
|
|
|
|
//see if it's in the cache
|
|
if (!_FFindCre(ctg, cno, pfnrpo, &icre) ||
|
|
rscNil != rsc && !_pcfl->FFind(kctgRsc, rsc))
|
|
{
|
|
return pvNil;
|
|
}
|
|
|
|
_pglcre->Get(icre, &cre);
|
|
AssertPo(cre.pbaco, 0);
|
|
cre.pbaco->AddRef();
|
|
return cre.pbaco;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
If the baco indicated chunk is cached, set its crep. Returns true
|
|
iff the baco was cached.
|
|
***************************************************************************/
|
|
bool CRF::FSetCrep(long crep, CTG ctg, CNO cno, PFNRPO pfnrpo, RSC rsc)
|
|
{
|
|
AssertThis(0);
|
|
Assert(pvNil != pfnrpo, "bad pfnrpo");
|
|
|
|
CRE cre;
|
|
long icre;
|
|
|
|
//see if it's in the cache
|
|
if (!_FFindCre(ctg, cno, pfnrpo, &icre) ||
|
|
rscNil != rsc && !_pcfl->FFind(kctgRsc, rsc))
|
|
{
|
|
return fFalse;
|
|
}
|
|
|
|
_pglcre->Get(icre, &cre);
|
|
AssertPo(cre.pbaco, 0);
|
|
cre.pbaco->SetCrep(crep);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Return this if the chunk is in this crf, otherwise return nil. The
|
|
caller is not given a reference count.
|
|
***************************************************************************/
|
|
PCRF CRF::PcrfFindChunk(CTG ctg, CNO cno, RSC rsc)
|
|
{
|
|
AssertThis(0);
|
|
|
|
if (!_pcfl->FFind(ctg, cno) ||
|
|
rscNil != rsc && !_pcfl->FFind(kctgRsc, rsc))
|
|
{
|
|
return pvNil;
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Check the _fAttached flag. If it's false, make sure the BACO is not
|
|
in the CRF.
|
|
***************************************************************************/
|
|
void CRF::BacoDetached(PBACO pbaco)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pbaco, 0);
|
|
Assert(pbaco->_pcrf == this, "BACO doesn't have right CRF");
|
|
long icre;
|
|
CRE cre;
|
|
|
|
if (pbaco->_fAttached)
|
|
{
|
|
Bug("who's calling BacoDetached?");
|
|
return;
|
|
}
|
|
if (!_FFindBaco(pbaco, &icre))
|
|
{
|
|
Bug("why isn't the BACO in the CRF?");
|
|
return;
|
|
}
|
|
_pglcre->Get(icre, &cre);
|
|
_cbCur -= cre.cb;
|
|
AssertIn(_cbCur, 0, kcbMax);
|
|
_pglcre->Delete(icre);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
The BACO was released. See if it should be flushed.
|
|
***************************************************************************/
|
|
void CRF::BacoReleased(PBACO pbaco)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pbaco, 0);
|
|
Assert(pbaco->_pcrf == this, "BACO doesn't have right CRF");
|
|
long icre;
|
|
CRE cre;
|
|
|
|
if (!pbaco->_fAttached || pbaco->CactRef() != 0)
|
|
{
|
|
Bug("who's calling BacoReleased?");
|
|
return;
|
|
}
|
|
|
|
if (!_FFindBaco(pbaco, &icre))
|
|
{
|
|
Bug("why isn't the BACO in the CRF?");
|
|
return;
|
|
}
|
|
_pglcre->Get(icre, &cre);
|
|
cre.cactRelease = _cactRelease++;
|
|
_pglcre->Put(icre, &cre);
|
|
|
|
if (pbaco->_crep <= crepToss || _cbCur > _cbMax)
|
|
{
|
|
//toss it
|
|
pbaco->Detach();
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Find the cre corresponding to the (ctg, cno, pfnrpo). Set *picre to
|
|
its location (or where it would be if it were in the list).
|
|
***************************************************************************/
|
|
bool CRF::_FFindCre(CTG ctg, CNO cno, PFNRPO pfnrpo, long *picre)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(picre);
|
|
CRE *qrgcre, *qcre;
|
|
long icreMin, icreLim, icre;
|
|
|
|
//Do a binary search. The CREs are sorted by (ctg, cno, pfnrpo).
|
|
qrgcre = (CRE *)_pglcre->QvGet(0);
|
|
for (icreMin = 0, icreLim = _pglcre->IvMac(); icreMin < icreLim; )
|
|
{
|
|
icre = (icreMin + icreLim) / 2;
|
|
qcre = qrgcre + icre;
|
|
AssertPo(qcre->pbaco, 0);
|
|
if (ctg < qcre->pbaco->_ctg)
|
|
icreLim = icre;
|
|
else if (ctg > qcre->pbaco->_ctg)
|
|
icreMin = icre + 1;
|
|
else if (cno < qcre->pbaco->_cno)
|
|
icreLim = icre;
|
|
else if (cno > qcre->pbaco->_cno)
|
|
icreMin = icre + 1;
|
|
else if (pfnrpo == qcre->pfnrpo)
|
|
{
|
|
*picre = icre;
|
|
return fTrue;
|
|
}
|
|
else if (pfnrpo < qcre->pfnrpo)
|
|
icreLim = icre;
|
|
else
|
|
icreMin = icre + 1;
|
|
}
|
|
|
|
*picre = icreMin;
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Find the cre corresponding to the BACO. Set *picre to its location.
|
|
***************************************************************************/
|
|
bool CRF::_FFindBaco(PBACO pbaco, long *picre)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pbaco, 0);
|
|
Assert(pbaco->_pcrf == this, "BACO doesn't have right CRF");
|
|
AssertVarMem(picre);
|
|
CTG ctg;
|
|
CNO cno;
|
|
CRE *qrgcre, *qcre;
|
|
long icreMin, icreLim, icre;
|
|
|
|
ctg = pbaco->_ctg;
|
|
cno = pbaco->_cno;
|
|
|
|
//Do a binary search. The CREs are sorted by (ctg, cno, pfnrpo).
|
|
qrgcre = (CRE *)_pglcre->QvGet(0);
|
|
for (icreMin = 0, icreLim = _pglcre->IvMac(); icreMin < icreLim; )
|
|
{
|
|
icre = (icreMin + icreLim) / 2;
|
|
qcre = qrgcre + icre;
|
|
AssertPo(qcre->pbaco, 0);
|
|
if (ctg < qcre->pbaco->_ctg)
|
|
icreLim = icre;
|
|
else if (ctg > qcre->pbaco->_ctg)
|
|
icreMin = icre + 1;
|
|
else if (cno < qcre->pbaco->_cno)
|
|
icreLim = icre;
|
|
else if (cno > qcre->pbaco->_cno)
|
|
icreMin = icre + 1;
|
|
else if (pbaco == qcre->pbaco)
|
|
{
|
|
*picre = icre;
|
|
return fTrue;
|
|
}
|
|
else
|
|
{
|
|
//we've found the (ctg, cno), now look for the BACO
|
|
for (icreMin = icre; icreMin-- > 0; )
|
|
{
|
|
qcre = qrgcre + icreMin;
|
|
if (qcre->pbaco->_ctg != ctg || qcre->pbaco->_cno != cno)
|
|
break;
|
|
if (qcre->pbaco == pbaco)
|
|
{
|
|
*picre = icreMin;
|
|
return fTrue;
|
|
}
|
|
}
|
|
for (icreLim = icre; ++icreLim < _pglcre->IvMac(); )
|
|
{
|
|
qcre = qrgcre + icreLim;
|
|
if (qcre->pbaco->_ctg != ctg || qcre->pbaco->_cno != cno)
|
|
break;
|
|
if (qcre->pbaco == pbaco)
|
|
{
|
|
*picre = icreLim;
|
|
return fTrue;
|
|
}
|
|
}
|
|
TrashVar(picre);
|
|
return fFalse;
|
|
}
|
|
}
|
|
|
|
TrashVar(picre);
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Try to purge at least cbPurge bytes of space. Doesn't free anything
|
|
with a crep > crepLast or that is locked.
|
|
***************************************************************************/
|
|
bool CRF::_FPurgeCb(long cbPurge, long crepLast)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(cbPurge, 1, kcbMax);
|
|
if (crepLast <= crepToss)
|
|
return fFalse;
|
|
|
|
CRE cre;
|
|
long icreMac;
|
|
|
|
while (0 < (icreMac = _pglcre->IvMac()))
|
|
{
|
|
// We want to find the "best" element to free. This is determined by
|
|
// keeping a "best so far" element, which we compare each element to.
|
|
// If the cre has a larger crep, it is worse, so just continue.
|
|
// If the cre has a smaller crep, it is better. When the crep values
|
|
// are the same, we score it based on when the cre was last released
|
|
// (how many releases have happened since the cre was last used) and
|
|
// how different the cb is from cbPurge. Each release is worth
|
|
// kcbRelease bytes. Bytes short of cbPurge are considered worse
|
|
// (by a factor of 3) than bytes beyond cbPurge, so we favor elements
|
|
// that are larger than cbPurge.
|
|
// REVIEW shonk: tune kcbRelease and the weighting factor...
|
|
const long kcbRelease = 256;
|
|
long icre, crep;
|
|
long lw, dcb;
|
|
long lwBest = klwMax;
|
|
long icreBest = ivNil;
|
|
long crepBest = crepLast;
|
|
|
|
for (icre = 0; icre < icreMac; icre++)
|
|
{
|
|
_pglcre->Get(icre, &cre);
|
|
AssertPo(cre.pbaco, 0);
|
|
if (cre.pbaco->CactRef() > 0 ||
|
|
(crep = cre.pbaco->_crep) > crepBest)
|
|
{
|
|
continue;
|
|
}
|
|
Assert(crep <= crepBest, 0);
|
|
AssertIn(cre.cactRelease, 0, _cactRelease);
|
|
|
|
dcb = cre.cb - cbPurge;
|
|
lw = -LwMul(kcbRelease, LwMin(kcbMax / kcbRelease,
|
|
_cactRelease - cre.cactRelease)) +
|
|
LwMul(2, LwAbs(dcb)) - dcb;
|
|
if (crep < crepBest || lw < lwBest)
|
|
{
|
|
icreBest = icre;
|
|
crepBest = crep;
|
|
lwBest = lw;
|
|
}
|
|
}
|
|
|
|
if (ivNil == icreBest)
|
|
return fFalse;
|
|
|
|
_pglcre->Get(icreBest, &cre);
|
|
Assert(cre.pbaco->_fAttached, "BACO not attached!");
|
|
cre.pbaco->Detach();
|
|
|
|
if (0 >= (cbPurge -= cre.cb))
|
|
return fTrue;
|
|
}
|
|
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Assert the validity of a CRF (chunky resource file).
|
|
***************************************************************************/
|
|
void CRF::AssertValid(ulong grf)
|
|
{
|
|
CRF_PAR::AssertValid(fobjAllocated);
|
|
AssertPo(_pglcre, 0);
|
|
AssertPo(_pcfl, 0);
|
|
AssertIn(_cbMax, 0, kcbMax);
|
|
AssertIn(_cbCur, 0, kcbMax);
|
|
AssertIn(_cactRelease, 0, kcbMax);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Mark memory used by a CRF.
|
|
***************************************************************************/
|
|
void CRF::MarkMem(void)
|
|
{
|
|
AssertThis(0);
|
|
long icre;
|
|
CRE cre;
|
|
|
|
CRF_PAR::MarkMem();
|
|
MarkMemObj(_pglcre);
|
|
MarkMemObj(_pcfl);
|
|
|
|
for (icre = _pglcre->IvMac(); icre-- > 0; )
|
|
{
|
|
_pglcre->Get(icre, &cre);
|
|
AssertPo(cre.pbaco, 0);
|
|
Assert(cre.pbaco->_fAttached, "baco claims to not be attached!");
|
|
cre.pbaco->_fAttached = fTrue; //safety to avoid infinite recursion
|
|
MarkMemObj(cre.pbaco);
|
|
}
|
|
}
|
|
#endif //DEBUG
|
|
|
|
|
|
/***************************************************************************
|
|
Destructor for Chunky resource manager.
|
|
***************************************************************************/
|
|
CRM::~CRM(void)
|
|
{
|
|
AssertBaseThis(fobjAllocated);
|
|
long ipcrf;
|
|
PCRF pcrf;
|
|
|
|
if (pvNil != _pglpcrf)
|
|
{
|
|
for (ipcrf = _pglpcrf->IvMac(); ipcrf-- > 0; )
|
|
{
|
|
_pglpcrf->Get(ipcrf, &pcrf);
|
|
AssertPo(pcrf, 0);
|
|
ReleasePpo(&pcrf);
|
|
}
|
|
ReleasePpo(&_pglpcrf);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Static method to create a new CRM.
|
|
***************************************************************************/
|
|
PCRM CRM::PcrmNew(long ccrfInit)
|
|
{
|
|
AssertIn(ccrfInit, 0, kcbMax);
|
|
PCRM pcrm;
|
|
|
|
if (pvNil == (pcrm = NewObj CRM()))
|
|
return pvNil;
|
|
if (pvNil == (pcrm->_pglpcrf = GL::PglNew(size(PCRF), ccrfInit)))
|
|
{
|
|
ReleasePpo(&pcrm);
|
|
return pvNil;
|
|
}
|
|
AssertPo(pcrm, 0);
|
|
return pcrm;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Prefetch the object if there is room in the cache. Assigns the fetched
|
|
object the given priority (crep).
|
|
***************************************************************************/
|
|
bool CRM::TLoad(CTG ctg, CNO cno, PFNRPO pfnrpo, RSC rsc, long crep)
|
|
{
|
|
AssertThis(0);
|
|
Assert(pvNil != pfnrpo, "nil object reader");
|
|
PCRF pcrf;
|
|
bool t;
|
|
long ipcrf;
|
|
long cpcrf = _pglpcrf->IvMac();
|
|
|
|
for (ipcrf = 0; ipcrf < cpcrf; ipcrf++)
|
|
{
|
|
_pglpcrf->Get(ipcrf, &pcrf);
|
|
AssertPo(pcrf, 0);
|
|
t = pcrf->TLoad(ctg, cno, pfnrpo, rsc, crep);
|
|
if (t != tNo)
|
|
return t;
|
|
}
|
|
return tNo;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Make sure the object is loaded and increment its reference count. If
|
|
successful, must be balanced with a call to ReleasePpo. If this fails,
|
|
and pfError is not nil, *pfError is set iff the chunk exists but
|
|
couldn't be loaded.
|
|
***************************************************************************/
|
|
PBACO CRM::PbacoFetch(CTG ctg, CNO cno, PFNRPO pfnrpo, bool *pfError, RSC rsc)
|
|
{
|
|
AssertThis(0);
|
|
Assert(pvNil != pfnrpo, "nil object reader");
|
|
AssertNilOrVarMem(pfError);
|
|
PCRF pcrf;
|
|
long ipcrf;
|
|
bool fError = fFalse;
|
|
PBACO pbaco = pvNil;
|
|
long cpcrf = _pglpcrf->IvMac();
|
|
|
|
for (ipcrf = 0; ipcrf < cpcrf; ipcrf++)
|
|
{
|
|
_pglpcrf->Get(ipcrf, &pcrf);
|
|
AssertPo(pcrf, 0);
|
|
pbaco = pcrf->PbacoFetch(ctg, cno, pfnrpo, &fError, rsc);
|
|
if (pvNil != pbaco || fError)
|
|
break;
|
|
}
|
|
if (pvNil != pfError)
|
|
*pfError = fError;
|
|
AssertNilOrPo(pbaco, 0);
|
|
return pbaco;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
If the object is loaded, increment its reference count and return it.
|
|
If it's not already loaded, just return nil.
|
|
***************************************************************************/
|
|
PBACO CRM::PbacoFind(CTG ctg, CNO cno, PFNRPO pfnrpo, RSC rsc)
|
|
{
|
|
AssertThis(0);
|
|
Assert(pvNil != pfnrpo, "nil object reader");
|
|
|
|
PCRF pcrf;
|
|
|
|
if (pvNil == (pcrf = PcrfFindChunk(ctg, cno, rsc)))
|
|
return pvNil;
|
|
|
|
return pcrf->PbacoFind(ctg, cno, pfnrpo);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
If the chunk is cached, set its crep. Returns true iff the chunk
|
|
was cached.
|
|
***************************************************************************/
|
|
bool CRM::FSetCrep(long crep, CTG ctg, CNO cno, PFNRPO pfnrpo, RSC rsc)
|
|
{
|
|
AssertThis(0);
|
|
Assert(pvNil != pfnrpo, "nil object reader");
|
|
PCRF pcrf;
|
|
long ipcrf;
|
|
long cpcrf = _pglpcrf->IvMac();
|
|
|
|
for (ipcrf = 0; ipcrf < cpcrf; ipcrf++)
|
|
{
|
|
_pglpcrf->Get(ipcrf, &pcrf);
|
|
AssertPo(pcrf, 0);
|
|
if (pcrf->FSetCrep(crep, ctg, cno, pfnrpo, rsc))
|
|
return fTrue;
|
|
}
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Return which CRF the given chunk is in. The caller is not given a
|
|
reference count.
|
|
***************************************************************************/
|
|
PCRF CRM::PcrfFindChunk(CTG ctg, CNO cno, RSC rsc)
|
|
{
|
|
AssertThis(0);
|
|
PCRF pcrf;
|
|
long ipcrf;
|
|
long cpcrf = _pglpcrf->IvMac();
|
|
|
|
for (ipcrf = 0; ipcrf < cpcrf; ipcrf++)
|
|
{
|
|
_pglpcrf->Get(ipcrf, &pcrf);
|
|
AssertPo(pcrf, 0);
|
|
|
|
if (pcrf->Pcfl()->FFind(ctg, cno) &&
|
|
(rscNil == rsc || pcrf->Pcfl()->FFind(kctgRsc, rsc)))
|
|
{
|
|
return pcrf;
|
|
}
|
|
}
|
|
|
|
return pvNil;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Add a chunky file to the list of chunky resource files, by
|
|
creating the chunky resource file object and adding it to the GL
|
|
***************************************************************************/
|
|
bool CRM::FAddCfl(PCFL pcfl, long cbMax, long *piv)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pcfl, 0);
|
|
AssertIn(cbMax, 0, kcbMax);
|
|
AssertNilOrVarMem(piv);
|
|
|
|
PCRF pcrf;
|
|
|
|
if (pvNil == (pcrf = CRF::PcrfNew(pcfl, cbMax)))
|
|
{
|
|
TrashVar(piv);
|
|
return fFalse;
|
|
}
|
|
if (!_pglpcrf->FAdd(&pcrf, piv))
|
|
{
|
|
ReleasePpo(&pcrf);
|
|
return fFalse;
|
|
}
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get the icrf'th CRF.
|
|
***************************************************************************/
|
|
PCRF CRM::PcrfGet(long icrf)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(icrf, 0, kcbMax);
|
|
PCRF pcrf;
|
|
|
|
if (!FIn(icrf, 0, _pglpcrf->IvMac()))
|
|
return pvNil;
|
|
|
|
_pglpcrf->Get(icrf, &pcrf);
|
|
AssertPo(pcrf, 0);
|
|
return pcrf;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Check the sanity of the CRM
|
|
***************************************************************************/
|
|
void CRM::AssertValid(ulong grfobj)
|
|
{
|
|
CRM_PAR::AssertValid(grfobj | fobjAllocated);
|
|
AssertPo(_pglpcrf, 0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
mark the memory associated with the CRM
|
|
***************************************************************************/
|
|
void CRM::MarkMem(void)
|
|
{
|
|
AssertThis(0);
|
|
long ipcrf;
|
|
long cpcrf;
|
|
PCRF pcrf;
|
|
|
|
CRM_PAR::MarkMem();
|
|
MarkMemObj(_pglpcrf);
|
|
|
|
for (ipcrf = 0, cpcrf = _pglpcrf->IvMac(); ipcrf < cpcrf; ipcrf++)
|
|
{
|
|
_pglpcrf->Get(ipcrf, &pcrf);
|
|
AssertPo(pcrf, 0);
|
|
MarkMemObj(pcrf);
|
|
}
|
|
}
|
|
#endif //DEBUG
|
|
|
|
|
|
/***************************************************************************
|
|
A PFNRPO to read GHQ objects.
|
|
***************************************************************************/
|
|
bool GHQ::FReadGhq(PCRF pcrf, CTG ctg, CNO cno, PBLCK pblck,
|
|
PBACO *ppbaco, long *pcb)
|
|
{
|
|
AssertPo(pcrf, 0);
|
|
AssertPo(pblck, 0);
|
|
AssertNilOrVarMem(ppbaco);
|
|
AssertVarMem(pcb);
|
|
GHQ *pghq;
|
|
HQ hq;
|
|
|
|
*pcb = pblck->Cb(fTrue);
|
|
if (pvNil == ppbaco)
|
|
return fTrue;
|
|
|
|
if (!pblck->FUnpackData() || hqNil == (hq = pblck->HqFree()))
|
|
{
|
|
TrashVar(pcb);
|
|
TrashVar(ppbaco);
|
|
return fFalse;
|
|
}
|
|
*pcb = CbOfHq(hq);
|
|
|
|
if (pvNil == (pghq = NewObj GHQ(hq)))
|
|
{
|
|
FreePhq(&hq);
|
|
TrashVar(pcb);
|
|
TrashVar(ppbaco);
|
|
return fFalse;
|
|
}
|
|
*ppbaco = pghq;
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Assert the validity of a GHQ.
|
|
***************************************************************************/
|
|
void GHQ::AssertValid(ulong grf)
|
|
{
|
|
GHQ_PAR::AssertValid(grf);
|
|
if (hqNil != hq)
|
|
AssertHq(hq);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Mark memory used by the GHQ.
|
|
***************************************************************************/
|
|
void GHQ::MarkMem(void)
|
|
{
|
|
GHQ_PAR::MarkMem();
|
|
MarkHq(hq);
|
|
}
|
|
#endif //DEBUG
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Assert the validity of a CABO.
|
|
***************************************************************************/
|
|
void CABO::AssertValid(ulong grf)
|
|
{
|
|
CABO_PAR::AssertValid(grf);
|
|
AssertNilOrPo(po, 0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Mark memory used by the CABO.
|
|
***************************************************************************/
|
|
void CABO::MarkMem(void)
|
|
{
|
|
CABO_PAR::MarkMem();
|
|
MarkMemObj(po);
|
|
}
|
|
#endif //DEBUG
|