/* 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; }