361 lines
8.2 KiB
C++
361 lines
8.2 KiB
C++
/* Copyright (c) Microsoft Corporation.
|
|
Licensed under the MIT License. */
|
|
|
|
/***************************************************************************
|
|
Author: ShonK
|
|
Project: Kauai
|
|
Reviewed:
|
|
Copyright (c) Microsoft Corporation
|
|
|
|
Codec manager class.
|
|
|
|
***************************************************************************/
|
|
#include "util.h"
|
|
ASSERTNAME
|
|
|
|
|
|
RTCLASS(CODM)
|
|
RTCLASS(CODC)
|
|
|
|
|
|
/***************************************************************************
|
|
The header on a compressed block consists of the cfmt (a long in big
|
|
endian order) and the decompressed length (a long in big endian order).
|
|
***************************************************************************/
|
|
const long kcbCodecHeader = 2 * size(long);
|
|
|
|
|
|
/***************************************************************************
|
|
Constructor for the compression manager. pcodc is an optional default
|
|
codec. cfmt is the default compression format.
|
|
***************************************************************************/
|
|
CODM::CODM(PCODC pcodc, long cfmt)
|
|
{
|
|
AssertNilOrPo(pcodc, 0);
|
|
Assert(cfmtNil != cfmt, "nil default compression format");
|
|
|
|
_cfmtDef = cfmt;
|
|
_pcodcDef = pcodc;
|
|
if (pvNil != _pcodcDef)
|
|
_pcodcDef->AddRef();
|
|
_pglpcodc = pvNil;
|
|
|
|
AssertThis(0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Destructor for the compression manager.
|
|
***************************************************************************/
|
|
CODM::~CODM(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
ReleasePpo(&_pcodcDef);
|
|
if (pvNil != _pglpcodc)
|
|
{
|
|
long ipcodc;
|
|
PCODC pcodc;
|
|
|
|
for (ipcodc = _pglpcodc->IvMac(); ipcodc-- > 0; )
|
|
{
|
|
_pglpcodc->Get(ipcodc, &pcodc);
|
|
ReleasePpo(&pcodc);
|
|
}
|
|
ReleasePpo(&_pglpcodc);
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Assert the validity of a CODM.
|
|
***************************************************************************/
|
|
void CODM::AssertValid(ulong grf)
|
|
{
|
|
CODM_PAR::AssertValid(0);
|
|
Assert(cfmtNil != _cfmtDef, "nil default compression");
|
|
AssertNilOrPo(_pcodcDef, 0);
|
|
AssertNilOrPo(_pglpcodc, 0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Mark memory for the CODM.
|
|
***************************************************************************/
|
|
void CODM::MarkMem(void)
|
|
{
|
|
AssertValid(0);
|
|
CODM_PAR::MarkMem();
|
|
|
|
MarkMemObj(_pcodcDef);
|
|
if (pvNil != _pglpcodc)
|
|
{
|
|
long ipcodc;
|
|
PCODC pcodc;
|
|
|
|
for (ipcodc = _pglpcodc->IvMac(); ipcodc-- > 0; )
|
|
{
|
|
_pglpcodc->Get(ipcodc, &pcodc);
|
|
MarkMemObj(pcodc);
|
|
}
|
|
MarkMemObj(_pglpcodc);
|
|
}
|
|
}
|
|
#endif //DEBUG
|
|
|
|
|
|
/***************************************************************************
|
|
Set the default compression type.
|
|
***************************************************************************/
|
|
void CODM::SetCfmtDefault(long cfmt)
|
|
{
|
|
AssertThis(0);
|
|
|
|
if (cfmt == cfmtNil)
|
|
{
|
|
Bug("can't set default compression to nil");
|
|
return;
|
|
}
|
|
|
|
_cfmtDef = cfmt;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Add a codec to the compression manager.
|
|
***************************************************************************/
|
|
bool CODM::FRegisterCodec(PCODC pcodc)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pcodc, 0);
|
|
|
|
if (pvNil == _pglpcodc && pvNil == (_pglpcodc = GL::PglNew(size(PCODC))))
|
|
return fFalse;
|
|
|
|
if (!_pglpcodc->FAdd(&pcodc))
|
|
return fFalse;
|
|
|
|
pcodc->AddRef();
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Return whether we can encode or decode the given format.
|
|
***************************************************************************/
|
|
bool CODM::FCanDo(long cfmt, bool fEncode)
|
|
{
|
|
AssertThis(0);
|
|
PCODC pcodc;
|
|
|
|
if (cfmtNil == cfmt)
|
|
return fFalse;
|
|
|
|
return _FFindCodec(fEncode, cfmt, &pcodc);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Gets the type of compression used on the block (assuming it is
|
|
compressed).
|
|
***************************************************************************/
|
|
bool CODM::FGetCfmtFromBlck(PBLCK pblck, long *pcfmt)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pblck, 0);
|
|
AssertVarMem(pcfmt);
|
|
|
|
byte rgb[4];
|
|
|
|
TrashVar(pcfmt);
|
|
if (pblck->Cb(fTrue) < 2 * size(rgb))
|
|
return fFalse;
|
|
|
|
if (!pblck->FReadRgb(rgb, size(rgb), 0, fTrue))
|
|
return fFalse;
|
|
|
|
*pcfmt = LwFromBytes(rgb[0], rgb[1], rgb[2], rgb[3]);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Look for a codec that can handle the given format.
|
|
***************************************************************************/
|
|
bool CODM::_FFindCodec(bool fEncode, long cfmt, PCODC *ppcodc)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(ppcodc);
|
|
Assert(cfmtNil != cfmt, "nil cfmt");
|
|
|
|
if (pvNil != _pcodcDef && _pcodcDef->FCanDo(fEncode, cfmt))
|
|
{
|
|
*ppcodc = _pcodcDef;
|
|
return fTrue;
|
|
}
|
|
|
|
if (pvNil != _pglpcodc)
|
|
{
|
|
long ipcodc;
|
|
PCODC pcodc;
|
|
|
|
for (ipcodc = _pglpcodc->IvMac(); ipcodc-- > 0; )
|
|
{
|
|
_pglpcodc->Get(ipcodc, &pcodc);
|
|
if (pcodc->FCanDo(fEncode, cfmt))
|
|
{
|
|
*ppcodc = pcodc;
|
|
return fTrue;
|
|
}
|
|
}
|
|
}
|
|
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Compress or decompress an hq of data. Note that the value of *phq
|
|
may change. cfmt should be cfmtNil to decompress.
|
|
***************************************************************************/
|
|
bool CODM::_FCodePhq(long cfmt, HQ *phq)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(phq);
|
|
AssertHq(*phq);
|
|
|
|
HQ hqDst;
|
|
void *pvSrc;
|
|
long cbSrc;
|
|
long cbDst;
|
|
bool fRet;
|
|
|
|
pvSrc = PvLockHq(*phq);
|
|
cbSrc = CbOfHq(*phq);
|
|
fRet = _FCode(cfmt, pvSrc, cbSrc, pvNil, 0, &cbDst);
|
|
UnlockHq(*phq);
|
|
if (!fRet)
|
|
return fFalse;
|
|
|
|
if (!FAllocHq(&hqDst, cbDst, fmemNil, mprNormal))
|
|
return fFalse;
|
|
|
|
pvSrc = PvLockHq(*phq);
|
|
fRet = _FCode(cfmt, pvSrc, cbSrc, PvLockHq(hqDst), cbDst, &cbDst);
|
|
UnlockHq(hqDst);
|
|
UnlockHq(*phq);
|
|
if (!fRet)
|
|
{
|
|
FreePhq(&hqDst);
|
|
return fFalse;
|
|
}
|
|
Assert(cbDst <= CbOfHq(hqDst), "why is the final size larger?");
|
|
if (cbDst < CbOfHq(hqDst))
|
|
AssertDo(FResizePhq(&hqDst, cbDst, fmemNil, mprNormal), 0);
|
|
|
|
FreePhq(phq);
|
|
*phq = hqDst;
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Compress or decompress a block of data. If pvDst is nil, just fill
|
|
*pcbDst with the required destination buffer size. This is just an
|
|
estimate in the compress case.
|
|
***************************************************************************/
|
|
bool CODM::_FCode(long cfmt, void *pvSrc, long cbSrc,
|
|
void *pvDst, long cbDst, long *pcbDst)
|
|
{
|
|
AssertIn(cbSrc, 1, kcbMax);
|
|
AssertPvCb(pvSrc, cbSrc);
|
|
AssertIn(cbDst, 0, kcbMax);
|
|
AssertPvCb(pvDst, cbDst);
|
|
AssertVarMem(pcbDst);
|
|
|
|
byte *prgb;
|
|
PCODC pcodc;
|
|
|
|
if (cfmtNil != cfmt)
|
|
{
|
|
// Encode the data
|
|
if (pvNil == pvDst || cbDst >= cbSrc)
|
|
cbDst = cbSrc - 1;
|
|
|
|
if (cbDst <= kcbCodecHeader)
|
|
{
|
|
// destination is smaller than the minimum compressed size, so
|
|
// no sense trying.
|
|
return fFalse;
|
|
}
|
|
|
|
// make sure we have a codec for this format
|
|
if (!_FFindCodec(fTrue, cfmt, &pcodc))
|
|
return fFalse;
|
|
|
|
if (pvNil == pvDst)
|
|
{
|
|
// this is our best guess at the compressed size
|
|
*pcbDst = cbDst;
|
|
return fTrue;
|
|
}
|
|
|
|
prgb = (byte *)pvDst;
|
|
if (!pcodc->FConvert(fTrue, cfmt, pvSrc, cbSrc, prgb + kcbCodecHeader,
|
|
cbDst - kcbCodecHeader, pcbDst))
|
|
{
|
|
return fFalse;
|
|
}
|
|
|
|
AssertIn(*pcbDst, 1, cbDst - kcbCodecHeader + 1);
|
|
*pcbDst += kcbCodecHeader;
|
|
|
|
prgb[0] = B3Lw(cfmt);
|
|
prgb[1] = B2Lw(cfmt);
|
|
prgb[2] = B1Lw(cfmt);
|
|
prgb[3] = B0Lw(cfmt);
|
|
prgb[4] = B3Lw(cbSrc);
|
|
prgb[5] = B2Lw(cbSrc);
|
|
prgb[6] = B1Lw(cbSrc);
|
|
prgb[7] = B0Lw(cbSrc);
|
|
}
|
|
else
|
|
{
|
|
// decode
|
|
if (0 >= (cbSrc -= kcbCodecHeader))
|
|
return fFalse;
|
|
|
|
// get the format and decompressed size
|
|
prgb = (byte *)pvSrc;
|
|
cfmt = LwFromBytes(prgb[0], prgb[1], prgb[2], prgb[3]);
|
|
*pcbDst = LwFromBytes(prgb[4], prgb[5], prgb[6], prgb[7]);
|
|
if (!FIn(*pcbDst, 1, pvNil == pvDst ? kcbMax : cbDst + 1))
|
|
return fFalse;
|
|
|
|
// make sure we have a codec for this format
|
|
if (!_FFindCodec(fFalse, cfmt, &pcodc))
|
|
return fFalse;
|
|
|
|
if (pvNil == pvDst)
|
|
return fTrue;
|
|
|
|
cbDst = *pcbDst;
|
|
if (!pcodc->FConvert(fFalse, cfmt,
|
|
prgb + kcbCodecHeader, cbSrc, pvDst, cbDst, pcbDst))
|
|
{
|
|
return fFalse;
|
|
}
|
|
|
|
if (cbDst != *pcbDst)
|
|
{
|
|
Bug("decompressed to wrong size");
|
|
return fFalse;
|
|
}
|
|
}
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|