Microsoft-3D-Movie-Maker/kauai/SRC/GROUPS2.CPP
2022-05-03 16:31:19 -07:00

1195 lines
30 KiB
C++

/* Copyright (c) Microsoft Corporation.
Licensed under the MIT License. */
/***************************************************************************
Author: ******
Project: Kauai
Reviewed:
Copyright (c) Microsoft Corporation
Basic collection classes (continued from groups.cpp):
General List (GL), Allocated List (AL),
General Group (GG), Allocated Group (AG),
General String Table (GST), Allocated String Table (AST).
BASE ---> GRPB -+-> GLB -+-> GL
| +-> AL
|
+-> GGB -+-> GG
| +-> AG
|
+-> GSTB-+-> GST
+-> AST
***************************************************************************/
#include "util.h"
ASSERTNAME
RTCLASS(GSTB)
RTCLASS(GST)
RTCLASS(AST)
/***************************************************************************
Constructor for a base string table.
***************************************************************************/
GSTB::GSTB(long cbExtra, ulong grfgst)
{
AssertIn(cbExtra, 0, kcbMax);
Assert(cbExtra % size(long) == 0, "cbExtra not multiple of size(long)");
_cbEntry = cbExtra + size(long);
_cbstFree = (grfgst & fgstAllowFree) ? 0 : cvNil;
// use some reasonable values for _cbMinGrow* - code can always set
// set these to something else
_cbMinGrow1 = 120;
_cbMinGrow2 = 12 * _cbEntry;
AssertThis(fobjAssertFull);
}
/***************************************************************************
Duplicate the string table.
***************************************************************************/
bool GSTB::_FDup(PGSTB pgstbDst)
{
AssertThis(fobjAssertFull);
AssertPo(pgstbDst, fobjAssertFull);
Assert(_cbEntry == pgstbDst->_cbEntry,
"why do these have different sized entries?");
if (!GSTB_PAR::_FDup(pgstbDst, _bstMac, LwMul(_cbEntry, _ivMac)))
return fFalse;
pgstbDst->_cbEntry = _cbEntry;
pgstbDst->_bstMac = _bstMac;
pgstbDst->_cbstFree = _cbstFree;
AssertPo(pgstbDst, fobjAssertFull);
return fTrue;
}
//string table on file
struct GSTF
{
short bo;
short osk;
long cbEntry;
long ibstMac;
long bstMac;
long cbstFree;
};
const BOM kbomGstf = 0x5FF00000L;
/***************************************************************************
Return the amount of space on file needed for the string table.
***************************************************************************/
long GSTB::CbOnFile(void)
{
AssertThis(0);
return size(GSTF) + LwMul(_ivMac, _cbEntry) + _bstMac;
}
/***************************************************************************
Write the string table to disk. It is the client's responsibility to
ensure that the extra data has the given byte order (bo) and osk.
If the osk has a different character size than the current one, the
strings are actually saved in the corresponding osk with the same sized
characters.
***************************************************************************/
bool GSTB::FWrite(PBLCK pblck, short bo, short osk)
{
AssertThis(fobjAssertFull);
AssertPo(pblck, 0);
Assert(kboCur == bo || kboOther == bo, "bad bo");
AssertOsk(osk);
GSTF gstf;
bool fRet;
if (osk != koskCur)
{
switch (osk)
{
case koskSbMac:
case koskUniMac:
osk = koskMac;
break;
case koskSbWin:
case koskUniWin:
osk = koskWin;
break;
default:
Bug("unknown osk");
return fFalse;
}
}
gstf.bo = kboCur;
gstf.osk = osk;
gstf.cbEntry = _cbEntry;
gstf.ibstMac = _ivMac;
gstf.bstMac = _bstMac;
gstf.cbstFree = _cbstFree;
if (kboOther == bo)
{
SwapBytesBom(&gstf, kbomGstf);
Assert(gstf.osk == osk, "osk not invariant under byte swapping");
_SwapBytesRgbst();
}
if (koskCur != osk)
_TranslateGrst(osk, fFalse);
fRet = _FWrite(pblck, &gstf, size(gstf), _bstMac, LwMul(_cbEntry, _ivMac));
if (koskCur != osk)
_TranslateGrst(osk, fTrue);
if (kboOther == bo)
_SwapBytesRgbst();
return fRet;
}
/***************************************************************************
Read string table data from a block.
***************************************************************************/
bool GSTB::_FRead(PBLCK pblck, short *pbo, short *posk)
{
AssertThis(0);
AssertPo(pblck, 0);
AssertNilOrVarMem(pbo);
AssertNilOrVarMem(posk);
GSTF gstf;
long cbT;
short bo;
long cb;
bool fRet = fFalse;
if (!pblck->FUnpackData())
goto LFail;
cb = pblck->Cb();
if (cb < size(gstf))
goto LBug;
if (!pblck->FReadRgb(&gstf, size(gstf), 0))
goto LFail;
if (pbo != pvNil)
*pbo = gstf.bo;
if (posk != pvNil)
*posk = gstf.osk;
if ((bo = gstf.bo) == kboOther)
SwapBytesBom(&gstf, kbomGstf);
cb -= size(gstf);
//don't use LwMul, in case the file is corrupted, we don't want to assert,
//we should detect it below.
cbT = gstf.cbEntry * gstf.ibstMac;
if (gstf.bo != kboCur || !FIn(gstf.cbEntry, size(long), kcbMax) ||
(gstf.cbEntry % size(long)) != 0 || !FIn(gstf.ibstMac, 0, kcbMax) ||
!FIn(gstf.bstMac, gstf.ibstMac - LwMax(0, gstf.cbstFree), kcbMax) ||
cb != cbT + gstf.bstMac ||
(gstf.cbstFree == cvNil) != (_cbstFree == cvNil) ||
gstf.cbstFree != cvNil && !FIn(gstf.cbstFree, 0, LwMax(1, gstf.ibstMac)))
{
LBug:
Warn("file corrupt or not a GSTB");
goto LFail;
}
_cbEntry = gstf.cbEntry;
_ivMac = gstf.ibstMac;
_bstMac = gstf.bstMac;
_cbstFree = gstf.cbstFree;
fRet = _FReadData(pblck, cb - cbT, cbT, size(gstf));
if (fRet)
{
if (bo == kboOther)
_SwapBytesRgbst();
if (koskCur != gstf.osk)
fRet = _FTranslateGrst(gstf.osk);
}
LFail:
TrashVarIf(!fRet, pbo);
TrashVarIf(!fRet, posk);
return fRet;
}
/***************************************************************************
Ensures that there is room to add at least cstnAdd new strings with
a total of cchAdd characters. If there is more than enough room and
fgrpShrink is passed, the GSTB may shrink.
***************************************************************************/
bool GSTB::FEnsureSpace(long cstnAdd, long cchAdd, ulong grfgrp)
{
AssertThis(0);
AssertIn(cstnAdd, 0, kcbMax);
AssertIn(cchAdd, 0, kcbMax);
long cbstAdd;
long cbAdd = cchAdd * size(achar);
if (cvNil == _cbstFree)
cbstAdd = cstnAdd;
else
cbstAdd = LwMax(0, cstnAdd - _cbstFree);
if (cbstAdd > kcbMax / _cbEntry - _ivMac ||
cstnAdd > kcbMax - _bstMac ||
cbAdd > kcbMax - _bstMac - cstnAdd)
{
Bug("why is this string table growing so large?");
return fFalse;
}
return _FEnsureSizes(_bstMac + cbAdd + cstnAdd,
LwMul(_ivMac + cbstAdd, _cbEntry), grfgrp);
}
/***************************************************************************
Set the minimum that a GSTB should grow by.
***************************************************************************/
void GSTB::SetMinGrow(long cstnAdd, long cchAdd)
{
AssertThis(0);
AssertIn(cstnAdd, 0, kcbMax);
AssertIn(cchAdd, 0, kcbMax);
_cbMinGrow1 = CbRoundToLong(cchAdd * size(achar) + cstnAdd);
_cbMinGrow2 = LwMul(cstnAdd, _cbEntry);
}
/***************************************************************************
Append an stn to string table.
***************************************************************************/
bool GSTB::FAddStn(PSTN pstn, void *pvExtra, long *pistn)
{
AssertThis(0);
AssertPo(pstn, 0);
return FAddRgch(pstn->Prgch(), pstn->Cch(), pvExtra, pistn);
}
/***************************************************************************
Replace the ith string.
***************************************************************************/
bool GSTB::FPutRgch(long istn, achar *prgch, long cch)
{
AssertThis(fobjAssertFull);
AssertIn(istn, 0, _ivMac);
Assert(!FFree(istn), "string entry is free!");
AssertIn(cch, 0, kcchMaxGst + 1);
AssertPvCb(prgch, cch * size(achar));
long cchOld;
long bstOld;
achar *qst;
qst = _Qst(istn);
if ((cchOld = CchSt(qst)) == cch)
{
CopyPb(prgch, PrgchSt(qst), cch * size(achar));
goto LDone;
}
if (cchOld < cch && !_FEnsureSizes(_bstMac + (cch - cchOld) * size(achar),
LwMul(_ivMac, _cbEntry), fgrpNil))
{
return fFalse;
}
// remove the old one
bstOld = _Bst(istn);
_RemoveSt(bstOld);
// append the new one
*_Qbst(istn) = _bstMac;
_AppendRgch(prgch, cch);
LDone:
AssertThis(fobjAssertFull);
return fTrue;
}
/***************************************************************************
Replace the ith string with stn.
***************************************************************************/
bool GSTB::FPutStn(long istn, PSTN pstn)
{
AssertThis(0);
AssertPo(pstn, 0);
return FPutRgch(istn, pstn->Prgch(), pstn->Cch());
}
/***************************************************************************
Get up to cchMax characters for the istn'th string.
***************************************************************************/
void GSTB::GetRgch(long istn, achar *prgch, long cchMax, long *pcch)
{
AssertThis(0);
AssertIn(istn, 0, _ivMac);
Assert(!FFree(istn), "string entry is free!");
AssertIn(cchMax, 0, kcbMax);
AssertPvCb(prgch, cchMax * size(achar));
AssertVarMem(pcch);
PST qst = _Qst(istn);
*pcch = LwMin(cchMax, CchSt(qst));
CopyPb(PrgchSt(qst), prgch, *pcch * size(achar));
}
/***************************************************************************
Get the ith string.
***************************************************************************/
void GSTB::GetStn(long istn, PSTN pstn)
{
AssertThis(0);
AssertIn(istn, 0, _ivMac);
Assert(!FFree(istn), "string entry is free!");
AssertPo(pstn, 0);
pstn->SetSt(_Qst(istn));
}
/***************************************************************************
Find the given stn in the string table.
***************************************************************************/
bool GSTB::FFindStn(PSTN pstn, long *pistn, ulong grfgst)
{
AssertThis(0);
AssertPo(pstn, 0);
return FFindRgch(pstn->Prgch(), pstn->Cch(), pistn, grfgst);
}
/***************************************************************************
Search for the string in the string table. This version does a linear
search. GST overrides this to do a binary search if fgstSorted is
passed in grfgst.
***************************************************************************/
bool GSTB::FFindRgch(achar *prgch, long cch, long *pistn, ulong grfgst)
{
AssertThis(0);
AssertIn(cch, 0, kcchMaxGst + 1);
AssertPvCb(prgch, cch * size(achar));
AssertVarMem(pistn);
long istn, bst;
PST qst;
for (istn = 0; istn < _ivMac; istn++)
{
bst = _Bst(istn);
if (bvNil == bst)
{
AssertIn(_cbstFree, 0, _ivMac);
Assert(istn < _ivMac - 1, "last element free");
continue;
}
AssertIn(bst, 0, _bstMac);
qst = (achar *)_Qb1(bst);
if (CchSt(qst) == cch && FEqualRgb(PrgchSt(qst), prgch, cch * size(achar)))
{
*pistn = istn;
return fTrue;
}
}
*pistn = istn;
return fFalse;
}
/***************************************************************************
Find the string with the given extra data in the string table.
***************************************************************************/
bool GSTB::FFindExtra(void *prgbFind, PSTN pstn, long *pistn)
{
AssertThis(0);
AssertPvCb(prgbFind, CbExtra());
AssertNilOrPo(pstn, 0);
AssertNilOrVarMem(pistn);
long istn;
byte *qbExtra;
long cbExtra = CbExtra();
long ivMac = IvMac();
if (cbExtra == 0)
{
Bug("no extra data");
TrashVar(pistn);
return fFalse;
}
for (istn = 0; istn < ivMac; istn++)
{
qbExtra = (byte *)_Qbst(istn) + size(long);
if (FEqualRgb(prgbFind, qbExtra, cbExtra))
{
if (pstn != pvNil)
GetStn(istn, pstn);
if (pistn != pvNil)
*pistn = istn;
return fTrue;
}
}
if (pvNil != pstn)
pstn->SetNil();
TrashVar(pistn);
return fFalse;
}
/***************************************************************************
Fetch the extra data for element istn.
***************************************************************************/
void GSTB::GetExtra(long istn, void *pv)
{
AssertThis(0);
AssertIn(istn, 0, _ivMac);
Assert(!FFree(istn), "string entry is free!");
Assert(_cbEntry > size(long), "no extra data");
AssertPvCb(pv, _cbEntry - size(long));
byte *qb;
qb = (byte *)_Qbst(istn) + size(long);
CopyPb(qb, pv, _cbEntry - size(long));
}
/***************************************************************************
Put the extra data for element istn.
***************************************************************************/
void GSTB::PutExtra(long istn, void *pv)
{
AssertThis(0);
AssertIn(istn, 0, _ivMac);
Assert(!FFree(istn), "string entry is free!");
Assert(_cbEntry > size(long), "no extra data");
AssertPvCb(pv, _cbEntry - size(long));
byte *qb;
qb = (byte *)_Qbst(istn) + size(long);
CopyPb(pv, qb, _cbEntry - size(long));
AssertThis(0);
}
/***************************************************************************
Return a volatile pointer to the string given the ibst (not the bst).
***************************************************************************/
achar *GSTB::_Qst(long ibst)
{
AssertIn(ibst, 0, _ivMac);
long bst = _Bst(ibst);
AssertIn(bst, 0, _bstMac);
return (achar *)_Qb1(bst);
}
/***************************************************************************
Private api to append the string. It's assumed that the first block
is already big enough to accomodate the string.
***************************************************************************/
void GSTB::_AppendRgch(achar *prgch, long cch)
{
AssertIn(cch, 0, kcchMaxGst + 1);
AssertPvCb(prgch, cch * size(achar));
achar *qch;
Assert(_Cb1() >= _bstMac + (cch + 1) * size(achar),
"first block not big enough");
qch = (achar *)_Qb1(_bstMac);
*qch++ = (achar)cch;
CopyPb(prgch, qch, cch * size(achar));
_bstMac += (cch + 1) * size(achar);
}
/***************************************************************************
Private api to remove the string.
***************************************************************************/
void GSTB::_RemoveSt(long bst)
{
AssertIn(bst, 0, _bstMac);
long cb;
byte *qb;
qb = _Qb1(bst);
cb = CchTotSt((PST)qb) * size(achar);
AssertIn(bst + cb, 0, _bstMac + 1);
if (bst + cb < _bstMac)
{
long cv;
BltPb(qb + cb, qb, _bstMac - bst - cb);
for (qb = _Qb2(0), cv = _ivMac; cv-- > 0; qb += _cbEntry)
{
if (*(long *)qb > bst)
{
Assert(*(long *)qb >= bst + cb, "overlapping strings in GSTB");
*(long *)qb -= cb;
}
}
}
_bstMac -= cb;
}
/***************************************************************************
Swap the bytes in the BST values. Note that each bst is followed
by the extra data, so we can't just use SwapBytesRglw.
***************************************************************************/
void GSTB::_SwapBytesRgbst(void)
{
if (size(long) == _cbEntry)
SwapBytesRglw(_Qb2(0), _ivMac);
else
{
long cbst;
byte *qb;
for (cbst = _ivMac, qb = _Qb2(0); cbst-- > 0; qb += _cbEntry)
SwapBytesRglw(qb, 1);
}
}
/***************************************************************************
Translate the strings to/from the platform osk. This only works if
CbCharOsk(osk) == CbCharOsk(koskCur) (it asserts otherwise).
***************************************************************************/
void GSTB::_TranslateGrst(short osk, bool fToCur)
{
AssertOsk(osk);
long bst;
byte *qb;
if (CbCharOsk(osk) != CbCharOsk(koskCur))
{
Bug("can't translate to this osk");
return;
}
qb = _Qb1(0);
for (bst = 0; bst < _bstMac; )
{
TranslateSt((achar *)(qb + bst), osk, fToCur);
bst += CchTotSt((PST)(qb + bst)) * size(achar);
}
}
/***************************************************************************
Translate the strings to the current osk.
***************************************************************************/
bool GSTB::_FTranslateGrst(short osk)
{
AssertOsk(osk);
void *pvSrc;
long cbSrc;
long cbChar;
long ibst, bstOld, cch;
long *qbst;
achar rgch[kcchMaxSt];
bool fRet = fFalse;
if ((cbChar = CbCharOsk(osk)) == CbCharOsk(koskCur))
{
_TranslateGrst(osk, fTrue);
return fTrue;
}
if (cbChar != size(schar) && cbChar != size(wchar))
{
Bug("unknown osk");
return fFalse;
}
if (_bstMac == 0)
return fTrue;
if (!FAllocPv(&pvSrc, cbSrc = _bstMac, fmemNil, mprNormal))
return fFalse;
CopyPb(_Qb1(0), pvSrc, cbSrc);
_bstMac = 0;
for (ibst = 0 ; ibst < _ivMac; ibst++)
{
qbst = _Qbst(ibst);
bstOld = *qbst;
*qbst = _bstMac;
if (bstOld + cbChar > cbSrc)
goto LFail;
if (cbChar == size(schar))
{
schar chs = *(schar *)PvAddBv(pvSrc, bstOld);
cch = (long)(byte)chs;
}
else
{
wchar chw;
CopyPb(PvAddBv(pvSrc, bstOld), &chw, size(wchar));
if (osk == MacWin(koskUniWin, koskUniMac))
SwapBytesRgsw(&chw, 1);
cch = (long)(ushort)chw;
}
if (!FIn(cch, 0, kcchMaxSt + 1) || bstOld + (cch + 1) * cbChar > cbSrc)
goto LFail;
cch = CchTranslateRgb(PvAddBv(pvSrc, bstOld + cbChar), cch * cbChar,
osk, rgch, kcchMaxSt);
if (!_FEnsureSizes(_bstMac + (cch + 1) * size(achar),
LwMul(_ivMac, _cbEntry), fgrpNil))
{
goto LFail;
}
_AppendRgch(rgch, cch);
}
fRet = fTrue;
AssertDo(_FEnsureSizes(_bstMac, LwMul(_ivMac, _cbEntry), fgrpShrink), 0);
LFail:
FreePpv(&pvSrc);
return fRet;
}
/***************************************************************************
Returns true iff ibst is out of range or the corresponding bst is
bvNil.
***************************************************************************/
bool GSTB::FFree(long istn)
{
AssertIn(istn, 0, kcbMax);
long bst;
if (!FIn(istn, 0, _ivMac))
return fTrue;
bst = _Bst(istn);
Assert(bvNil == bst || FIn(bst, 0, _bstMac), "bad bst");
return bvNil == bst;
}
#ifdef DEBUG
/***************************************************************************
Validate a string table.
***************************************************************************/
void GSTB::AssertValid(ulong grfobj)
{
long ibst;
long cchTot, cbstFree;
long bst;
GSTB_PAR::AssertValid(grfobj);
AssertIn(_cbEntry, size(long), kcbMax);
Assert(_cbEntry % size(long) == 0, "_cbEntry bad");
AssertIn(_ivMac, 0, kcbMax);
Assert(_Cb1() >= _bstMac, "grst area too small");
Assert(_Cb2() >= LwMul(_ivMac, _cbEntry), "rgbst area too small");
Assert(_cbstFree == cvNil || FIn(_cbstFree, 0, LwMax(1, _ivMac)),
"_cbstFree is wrong");
if (grfobj & fobjAssertFull)
{
// it would be nice to assert that no strings overlap each other,
// but that's hard to do in linear time, so just assert that the
// grst is the correct size.
for (cbstFree = cchTot = ibst = 0; ibst < _ivMac; ibst++)
{
bst = _Bst(ibst);
if (bvNil == bst)
{
Assert(ibst < _ivMac - 1, "last element free");
cbstFree++;
continue;
}
AssertIn(bst, 0, _bstMac);
AssertSt((achar *)_Qb1(bst));
cchTot += CchTotSt((PST)_Qb1(bst));
}
Assert(cchTot * size(achar) == _bstMac, "grst wrong size");
Assert(cbstFree == _cbstFree || _cbstFree == cvNil && cbstFree == 0,
"bad _cbstFree");
}
}
#endif //DEBUG
/***************************************************************************
Allocate a new string table and ensure that it has space for cstnInit
strings, totalling cchInit characters.
***************************************************************************/
PGST GST::PgstNew(long cbExtra, long cstnInit, long cchInit)
{
AssertIn(cbExtra, 0, kcbMax);
Assert(cbExtra % size(long) == 0, "cbExtra not multiple of size(long)");
AssertIn(cstnInit, 0, kcbMax);
AssertIn(cchInit, 0, kcbMax);
PGST pgst;
if ((pgst = NewObj GST(cbExtra)) == pvNil)
return pvNil;
if ((cstnInit > 0 || cchInit > 0) &&
!pgst->FEnsureSpace(cstnInit, cchInit, fgrpNil))
{
ReleasePpo(&pgst);
return pvNil;
}
AssertPo(pgst, 0);
return pgst;
}
/***************************************************************************
Read a string table from a block and return it.
***************************************************************************/
PGST GST::PgstRead(PBLCK pblck, short *pbo, short *posk)
{
AssertPo(pblck, 0);
AssertNilOrVarMem(pbo);
AssertNilOrVarMem(posk);
PGST pgst;
if ((pgst = NewObj GST(0)) == pvNil)
goto LFail;
if (!pgst->_FRead(pblck, pbo, posk))
{
ReleasePpo(&pgst);
LFail:
TrashVar(pbo);
TrashVar(posk);
return pvNil;
}
AssertPo(pgst, 0);
return pgst;
}
/***************************************************************************
Read a string table from file and return it.
***************************************************************************/
PGST GST::PgstRead(PFIL pfil, FP fp, long cb, short *pbo, short *posk)
{
BLCK blck(pfil, fp, cb);
return PgstRead(&blck, pbo, posk);
}
/***************************************************************************
Duplicate this GST.
***************************************************************************/
PGST GST::PgstDup(void)
{
AssertThis(0);
PGST pgst;
if (pvNil == (pgst = PgstNew(_cbEntry - size(long))))
return pvNil;
if (!_FDup(pgst))
ReleasePpo(&pgst);
AssertNilOrPo(pgst, 0);
return pgst;
}
/***************************************************************************
Append a string to the string table.
***************************************************************************/
bool GST::FAddRgch(achar *prgch, long cch, void *pvExtra, long *pistn)
{
AssertThis(0);
AssertIn(cch, 0, kcchMaxGst + 1);
AssertPvCb(prgch, cch * size(achar));
AssertNilOrPvCb(pvExtra, _cbEntry - size(long));
AssertNilOrVarMem(pistn);
if (pistn != pvNil)
*pistn = _ivMac;
if (!FInsertRgch(_ivMac, prgch, cch, pvExtra))
{
TrashVar(pistn);
return fFalse;
}
return fTrue;
}
/***************************************************************************
Find the given string and put its location in *pistn. If it's not
there, fill *pistn with where it would be. If fgstSorted or fgstUserSorted
is passed in, this does a binary search for the string; otherwise it
does a linear search.
***************************************************************************/
bool GST::FFindRgch(achar *prgch, long cch, long *pistn, ulong grfgst)
{
AssertThis(0);
AssertIn(cch, 0, kcchMaxGst);
AssertPvCb(prgch, cch * size(achar));
AssertVarMem(pistn);
if (!(grfgst & (fgstSorted | fgstUserSorted)))
return GSTB::FFindRgch(prgch, cch, pistn, grfgst);
//the table should be sorted, so do a binary search
long ivMin, ivLim, iv;
ulong fcmp;
achar *qst;
for (ivMin = 0, ivLim = _ivMac; ivMin < ivLim; )
{
iv = (ivMin + ivLim) / 2;
qst = _Qst(iv);
if (grfgst & fgstUserSorted)
fcmp = FcmpCompareUserRgch(prgch, cch, PrgchSt(qst), CchSt(qst));
else
fcmp = FcmpCompareRgch(prgch, cch, PrgchSt(qst), CchSt(qst));
if (fcmp == fcmpLt)
ivLim = iv;
else if (fcmp == fcmpGt)
ivMin = iv + 1;
else
{
Assert(fcmp == fcmpEq, "bad fcmp");
*pistn = iv;
return fTrue;
}
}
// this is where it would be
*pistn = ivMin;
return fFalse;
}
/***************************************************************************
Insert a new entry into the string text.
***************************************************************************/
bool GST::FInsertRgch(long istn, achar *prgch, long cch, void *pvExtra)
{
AssertThis(fobjAssertFull);
AssertIn(istn, 0, _ivMac + 1);
AssertIn(cch, 0, kcchMaxGst + 1);
AssertPvCb(prgch, cch * size(achar));
AssertNilOrPvCb(pvExtra, _cbEntry - size(long));
byte *qb;
if (!_FEnsureSizes(_bstMac + (cch + 1) * size(achar),
LwMul(_ivMac + 1, _cbEntry), fgrpNil))
{
return fFalse;
}
// make room for the entry
qb = (byte *)_Qbst(istn);
if (istn < _ivMac)
BltPb(qb, qb + _cbEntry, LwMul(_ivMac - istn, _cbEntry));
*(long *)qb = _bstMac;
if (size(long) < _cbEntry)
{
qb += size(long);
if (pvExtra != pvNil)
CopyPb(pvExtra, qb, _cbEntry - size(long));
else
TrashPvCb(qb, _cbEntry - size(long));
}
else
Assert(pvNil == pvExtra, "cbExtra is zero");
_ivMac++;
// put the string in
_AppendRgch(prgch, cch);
AssertThis(fobjAssertFull);
return fTrue;
}
/***************************************************************************
Insert an stn into the string table
***************************************************************************/
bool GST::FInsertStn(long istn, PSTN pstn, void *pvExtra)
{
AssertThis(0);
AssertIn(istn, 0, _ivMac + 1);
AssertPo(pstn, 0);
AssertNilOrPvCb(pvExtra, _cbEntry - size(long));
return FInsertRgch(istn, pstn->Prgch(), pstn->Cch(), pvExtra);
}
/***************************************************************************
Delete the string at location istn.
***************************************************************************/
void GST::Delete(long istn)
{
AssertThis(fobjAssertFull);
AssertIn(istn, 0, _ivMac);
byte *qb;
long bst;
qb = (byte *)_Qbst(istn);
bst = *(long *)qb;
if (istn < --_ivMac)
BltPb(qb + _cbEntry, qb, LwMul(_ivMac - istn, _cbEntry));
TrashPvCb(_Qbst(_ivMac), _cbEntry);
_RemoveSt(bst);
AssertThis(fobjAssertFull);
}
/***************************************************************************
Move the entry at ivSrc to be immediately before the element that is
currently at ivTarget. If ivTarget > ivSrc, the entry actually ends
up at (ivTarget - 1) and the entry at ivTarget doesn't move. If
ivTarget < ivSrc, the entry ends up at ivTarget and the entry at
ivTarget moves to (ivTarget + 1). Everything in between is shifted
appropriately. ivTarget is allowed to be equal to IvMac().
***************************************************************************/
void GST::Move(long ivSrc, long ivTarget)
{
AssertThis(0);
AssertIn(ivSrc, 0, _ivMac);
AssertIn(ivTarget, 0, _ivMac + 1);
MoveElement(_Qbst(0), _cbEntry, ivSrc, ivTarget);
AssertThis(0);
}
#ifdef DEBUG
/***************************************************************************
Validate a string table.
***************************************************************************/
void GST::AssertValid(ulong grfobj)
{
GST_PAR::AssertValid(grfobj);
AssertVar(_cbstFree == cvNil, "bad _cbstFree in GST", &_cbstFree);
}
#endif //DEBUG
/***************************************************************************
Allocate a new allocated string table and ensure that it has space for
cstnInit strings, totalling cchInit characters.
***************************************************************************/
PAST AST::PastNew(long cbExtra, long cstnInit, long cchInit)
{
AssertIn(cbExtra, 0, kcbMax);
Assert(cbExtra % size(long) == 0, "cbExtra not multiple of size(long)");
AssertIn(cstnInit, 0, kcbMax);
AssertIn(cchInit, 0, kcbMax);
PAST past;
if ((past = NewObj AST(cbExtra)) == pvNil)
return pvNil;
if ((cstnInit > 0 || cchInit > 0) &&
!past->FEnsureSpace(cstnInit, cchInit, fgrpNil))
{
ReleasePpo(&past);
return pvNil;
}
AssertPo(past, 0);
return past;
}
/***************************************************************************
Read an allocated string table from a block and return it.
***************************************************************************/
PAST AST::PastRead(PBLCK pblck, short *pbo, short *posk)
{
AssertPo(pblck, 0);
AssertNilOrVarMem(pbo);
AssertNilOrVarMem(posk);
PAST past;
if ((past = NewObj AST(0)) == pvNil)
goto LFail;
if (!past->_FRead(pblck, pbo, posk))
{
ReleasePpo(&past);
LFail:
TrashVar(pbo);
TrashVar(posk);
return pvNil;
}
AssertPo(past, 0);
return past;
}
/***************************************************************************
Read an allocated string table from file and return it.
***************************************************************************/
PAST AST::PastRead(PFIL pfil, FP fp, long cb, short *pbo, short *posk)
{
BLCK blck;
return PastRead(&blck, pbo, posk);
}
/***************************************************************************
Duplicate this AST.
***************************************************************************/
PAST AST::PastDup(void)
{
AssertThis(0);
PAST past;
if (pvNil == (past = PastNew(_cbEntry - size(long))))
return pvNil;
if (!_FDup(past))
ReleasePpo(&past);
AssertNilOrPo(past, 0);
return past;
}
/***************************************************************************
Append a string to the allocated string table.
***************************************************************************/
bool AST::FAddRgch(achar *prgch, long cch, void *pvExtra, long *pistn)
{
AssertThis(fobjAssertFull);
AssertIn(cch, 0, kcchMaxGst + 1);
AssertPvCb(prgch, cch * size(achar));
AssertNilOrPvCb(pvExtra, _cbEntry - size(long));
AssertNilOrVarMem(pistn);
long ibst;
byte *qb;
if (_cbstFree > 0)
{
/* find the first free bst */
qb = _Qb2(0);
for (ibst = 0; ibst < _ivMac; ibst++, qb += _cbEntry)
{
if (*(long *)qb == bvNil)
break;
}
Assert(ibst < _ivMac - 1, "_cbstFree is wrong");
_cbstFree--;
}
else
ibst = _ivMac++;
if (!_FEnsureSizes(_bstMac + (cch + 1) * size(achar),
LwMul(_ivMac, _cbEntry), fgrpNil))
{
if (ibst == _ivMac - 1)
_ivMac--;
else
_cbstFree++;
TrashVar(pistn);
return fFalse;
}
// fill in the bst and extra data
qb = (byte *)_Qbst(ibst);
*(long *)qb = _bstMac;
if (size(long) < _cbEntry)
{
qb += size(long);
if (pvExtra != pvNil)
CopyPb(pvExtra, qb, _cbEntry - size(long));
else
TrashPvCb(qb, _cbEntry - size(long));
}
else
Assert(pvNil == pvExtra, "cbExtra is zero");
// put the string in
_AppendRgch(prgch, cch);
if (pvNil != pistn)
*pistn = ibst;
AssertThis(fobjAssertFull);
return fTrue;
}
/***************************************************************************
Delete the string at location istn.
***************************************************************************/
void AST::Delete(long istn)
{
AssertThis(fobjAssertFull);
AssertIn(istn, 0, _ivMac);
Assert(!FFree(istn), "entry already free!");
byte *qb;
long bst;
qb = (byte *)_Qbst(istn);
bst = *(long *)qb;
if (istn == _ivMac - 1)
{
//move _ivMac back past any free entries on the end
while (--_ivMac > 0 && *(long *)(qb -= _cbEntry) == bvNil)
_cbstFree--;
TrashPvCb(_Qbst(_ivMac), LwMul(istn - _ivMac + 1, _cbEntry));
}
else
{
*(long *)qb = bvNil;
TrashPvCb(qb + size(long), _cbEntry - size(long));
_cbstFree++;
}
_RemoveSt(bst);
AssertThis(fobjAssertFull);
}
#ifdef DEBUG
/***************************************************************************
Validate a string table.
***************************************************************************/
void AST::AssertValid(ulong grfobj)
{
AST_PAR::AssertValid(grfobj);
AssertIn(_cbstFree, 0, LwMax(1, _ivMac));
}
#endif //DEBUG