mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
522 lines
13 KiB
522 lines
13 KiB
// SaveLoad.cpp -- Implementation for the class CPersist
|
|
|
|
#include "stdafx.h"
|
|
#include "SaveLoad.h"
|
|
#include "Memex.h"
|
|
#include "String.h"
|
|
#include "stdlib.h"
|
|
#include "search.h"
|
|
#include "memory.h"
|
|
#include "Except.h"
|
|
|
|
CPersist::CPersist()
|
|
{
|
|
m_puio = NULL;
|
|
m_pios = NULL;
|
|
m_pdwImage = NULL;
|
|
m_pdwNextTable = NULL;
|
|
m_szFile[0] = 0;
|
|
|
|
m_fExceptionCleanup = FALSE;
|
|
|
|
ZeroMemory(&m_vb, sizeof(m_vb));
|
|
}
|
|
|
|
CPersist::~CPersist()
|
|
{
|
|
if (m_pios)
|
|
{
|
|
if (m_fExceptionCleanup)
|
|
{
|
|
m_pios->ExceptionDestructor(); m_pios= NULL;
|
|
}
|
|
else
|
|
{
|
|
if (m_vb.Base && m_pdwNextTable > PUINT(m_vb.Base))
|
|
CompleteDiskImage();
|
|
|
|
delete m_pios; m_pios= NULL;
|
|
}
|
|
}
|
|
|
|
if (m_pdwImage) ReleaseImage();
|
|
|
|
ASSERT(!m_pdwImage);
|
|
|
|
if (m_puio) delete m_puio;
|
|
|
|
if (m_vb.Base) FreeVirtualBuffer(&m_vb);
|
|
}
|
|
|
|
void CPersist::CompleteDiskImage()
|
|
{
|
|
// ASSERT(m_vb.Base && m_pdwNextTable > PUINT(m_vb.Base)); //rmk
|
|
ASSERT(m_pios);
|
|
|
|
UINT offTable= NextOffset();
|
|
|
|
WriteDWords(PUINT(m_vb.Base), m_pdwNextTable - PUINT(m_vb.Base));
|
|
|
|
m_pdwNextTable= PUINT(m_vb.Base);
|
|
|
|
m_pios->FlushOutput(TRUE);
|
|
|
|
UINT cdwFile= m_pios->CdwStream();
|
|
|
|
delete m_pios; m_pios= NULL;
|
|
|
|
UINT cbSector= m_puio->CbSector();
|
|
|
|
if (cbSector < CSLOTS * sizeof(UINT))
|
|
cbSector = cbSector * ((CSLOTS * sizeof(UINT) + cbSector - 1) / cbSector);
|
|
|
|
PUINT pdwSector = NULL;
|
|
|
|
__try
|
|
{
|
|
pdwSector= (PUINT) m_puio->GetBuffer(&cbSector);
|
|
|
|
m_puio->Read(pdwSector, 0, 0, cbSector);
|
|
|
|
pdwSector[ISLOT_TABLE_OFFSET]= offTable;
|
|
|
|
m_puio->Write(pdwSector, 0, 0, cbSector);
|
|
|
|
m_puio->MakePermanent(m_szFile, TRUE, cdwFile * sizeof(UINT));
|
|
}
|
|
__finally
|
|
{
|
|
if (pdwSector) m_puio->FreeBuffer(pdwSector);
|
|
}
|
|
}
|
|
|
|
CPersist *CPersist::CreateDiskImage(PSZ pszFileName, UINT dwOptions, UINT usSignature, UINT usVersion)
|
|
{
|
|
ASSERT(pszFileName);
|
|
ASSERT(strlen(pszFileName) <= MAX_PATH);
|
|
|
|
// BugBug: Should verify the syntax of pszFileName here.
|
|
// We may be able to use GetFullPathName to do this.
|
|
|
|
CPersist *pp= NULL;
|
|
|
|
__try
|
|
{
|
|
pp= New CPersist;
|
|
|
|
strcpy(pp->m_szFile, pszFileName);
|
|
|
|
CreateVirtualBuffer(&(pp->m_vb), 0x10000, 0x800000);
|
|
|
|
pp->m_pdwNextTable= (PUINT) (pp->m_vb.Base);
|
|
|
|
pp->m_puio= CUnbufferedIO::NewTempFile(pszFileName, TRUE);
|
|
|
|
pp->m_pios= CIOStream::NewIOStream(pp->m_puio);
|
|
|
|
pp->m_pios->AttachStream(TRUE, 0, 0);
|
|
|
|
pp->m_pios->PutDWordOut(usSignature); // To identify this as a Full-Text index
|
|
pp->m_pios->PutDWordOut(usVersion ); // Version number for this file layout
|
|
pp->m_pios->PutDWordOut(dwOptions ); // Place holder for Index creation options
|
|
pp->m_pios->PutDWordOut(UINT(-1) ); // Place holder for offset to content directory
|
|
}
|
|
__finally
|
|
{
|
|
if (_abnormal_termination() && pp)
|
|
{
|
|
pp->m_fExceptionCleanup= TRUE; delete pp; pp= NULL;
|
|
}
|
|
}
|
|
|
|
return pp;
|
|
}
|
|
|
|
void CPersist::ExceptionDestructor()
|
|
{
|
|
m_fExceptionCleanup= TRUE; delete this;
|
|
}
|
|
|
|
BOOL CPersist::IsValidIndex(PSZ pszFileName, UINT dwOptions)
|
|
{
|
|
CPersist *pPersist = NULL;
|
|
UINT fOptions = 0;
|
|
__try
|
|
{
|
|
pPersist= OpenDiskImage(pszFileName);
|
|
|
|
fOptions= pPersist->m_pdwImage[ISLOT_OPTIONS];
|
|
|
|
delete pPersist; pPersist= NULL;
|
|
}
|
|
__except(FilterFTExceptions(_exception_code()))
|
|
{
|
|
if (pPersist) delete pPersist;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return fOptions == dwOptions;
|
|
}
|
|
|
|
|
|
CPersist *CPersist::OpenDiskImage(PSZ pszFileName, UINT usSignature, UINT usVersion, UINT usMinVersion)
|
|
{
|
|
ASSERT(pszFileName);
|
|
ASSERT(strlen(pszFileName) <= MAX_PATH);
|
|
|
|
CPersist *pp = NULL;
|
|
BOOL fOpenFailed = FALSE;
|
|
|
|
__try
|
|
{
|
|
pp= New CPersist();
|
|
|
|
strcpy(pp->m_szFile, pszFileName);
|
|
|
|
for (;;)
|
|
{
|
|
pp->m_puio= CUnbufferedIO::OpenExistingFile(pszFileName);
|
|
|
|
if (!(pp->m_puio)) { fOpenFailed= TRUE; __leave; }
|
|
|
|
pp->m_pdwImage= PUINT(pp->m_puio->MappedImage());
|
|
|
|
if ( pp->m_pdwImage
|
|
&& pp->m_pdwImage[ISLOT_SIGNATURE ] == usSignature
|
|
&& pp->m_pdwImage[ISLOT_FTS_VERSION ] <= usVersion
|
|
&& pp->m_pdwImage[ISLOT_FTS_VERSION ] >= usMinVersion
|
|
&& pp->m_pdwImage[ISLOT_TABLE_OFFSET] != UINT(-1)
|
|
) break;
|
|
|
|
#ifdef _DEBUG
|
|
char ac[500];
|
|
|
|
if (!(pp->m_pdwImage)) wsprintf(ac, "Can't map file \"%s\" into memory!", pp->m_szFile);
|
|
else if ( pp->m_pdwImage[ISLOT_SIGNATURE ] != usSignature
|
|
|| pp->m_pdwImage[ISLOT_FTS_VERSION] != usVersion
|
|
)
|
|
wsprintf(ac, "Signature == 0x%08x, should be 0x%08x; File Version == 0x%08x, should be 0x%08x",
|
|
pp->m_pdwImage[ISLOT_SIGNATURE], usSignature, pp->m_pdwImage[ISLOT_FTS_VERSION], usVersion
|
|
);
|
|
else if (pp->m_pdwImage[ISLOT_TABLE_OFFSET] == UINT(-1))
|
|
wsprintf(ac, "Damaged index file: Table Offset == 0x%08x",
|
|
pp->m_pdwImage[ISLOT_TABLE_OFFSET]
|
|
);
|
|
|
|
::MessageBox(::hwndMain, ac, "Invalid Index File!", MB_OK);
|
|
#endif // _DEBUG
|
|
|
|
SetLastError( (!pp->m_pdwImage) ? ERR_FILE_MAP_FAILED :
|
|
( pp->m_pdwImage[ISLOT_SIGNATURE ] != usSignature
|
|
|| pp->m_pdwImage[ISLOT_FTS_VERSION] < usMinVersion
|
|
) ? ERR_INVALID_FILE_TYPE :
|
|
(pp->m_pdwImage[ISLOT_FTS_VERSION] > usVersion)
|
|
? ERR_FUTURE_VERSION : ERR_DAMAGED_FILE
|
|
);
|
|
|
|
delete pp->m_puio; pp->m_puio= NULL; pp->m_pdwImage= NULL;
|
|
|
|
fOpenFailed= TRUE; __leave;
|
|
}
|
|
|
|
pp->m_pdwNextTable= pp->m_pdwImage + pp->m_pdwImage[ISLOT_TABLE_OFFSET];
|
|
}
|
|
__finally
|
|
{
|
|
if ((fOpenFailed || _abnormal_termination()) && pp)
|
|
{
|
|
pp->m_fExceptionCleanup= TRUE; delete pp; pp= NULL;
|
|
}
|
|
}
|
|
|
|
return pp;
|
|
}
|
|
|
|
void CPersist::ReleaseImage()
|
|
{
|
|
ASSERT(m_pdwImage && m_puio && !m_pios);
|
|
|
|
m_puio->UnmapImage(); m_pdwImage= NULL;
|
|
|
|
delete m_puio; m_puio= NULL;
|
|
}
|
|
|
|
|
|
PVOID CPersist::ReserveTableSpace(UINT cbTable)
|
|
{
|
|
UINT cdw= (cbTable + sizeof(UINT) - 1) / sizeof(UINT);
|
|
PUINT pdw= m_pdwNextTable + cdw;
|
|
|
|
if (m_vb.Base) // Non-null when we're saving to disk
|
|
// Null when we're reloading from disk
|
|
if (PBYTE(pdw) - 1 > PBYTE(m_vb.CommitLimit))
|
|
ExtendVirtualBuffer(&m_vb, PBYTE(pdw) - 1);
|
|
|
|
m_pdwNextTable= pdw;
|
|
|
|
return (PVOID) (pdw - cdw);
|
|
}
|
|
|
|
|
|
int __cdecl comparer (const void *pvL, const void *pvR)
|
|
{
|
|
return *((LPINT) pvR) - *((LPINT)pvL);
|
|
}
|
|
|
|
|
|
UINT CPersist::Encode(const BYTE *pbData, UINT cbData)
|
|
{
|
|
enum {MAX_WORK_BUFFER = 0x4000};
|
|
enum {BREAK_POINT = 3};
|
|
enum {SHIFT_BITS = 2};
|
|
enum {NUMBITS = sizeof(UINT)<<3};
|
|
|
|
UINT i, j, n;
|
|
|
|
UINT nBitsRemain;
|
|
UINT nWriteCount = 0;
|
|
UINT nCode, nCodeLen;
|
|
UINT nBits, nStart;
|
|
BYTE (*pbLookup)[16];
|
|
|
|
pbLookup = NULL;
|
|
PUINT pnCode = NULL;
|
|
PUINT table = NULL;
|
|
PBYTE pattern = NULL;
|
|
PBYTE bitcount = NULL;
|
|
|
|
ValidateHeap();
|
|
|
|
__try
|
|
{
|
|
table = New UINT[512];
|
|
pattern = New BYTE[256];
|
|
bitcount = New BYTE[256];
|
|
|
|
ValidateHeap();
|
|
|
|
for (j = 0; j < cbData; j++) // count byte frequencies in
|
|
table[2 * pbData[j]]++; // ... first entry of pair
|
|
|
|
ValidateHeap();
|
|
|
|
for (i = 0; i < 256; i++) // note character byte in
|
|
{ // ... second entry of pair
|
|
table[2*i+1] = i;
|
|
pattern[i] = bitcount[i] = 0; // initialization
|
|
}
|
|
|
|
ValidateHeap();
|
|
|
|
qsort(table, 256, 2*sizeof(UINT), comparer); // sort by byte frequency
|
|
|
|
ValidateHeap();
|
|
|
|
for (n = 0; n < 256 && table[n]; n += 2) {}; // count of non-zero frequencies
|
|
|
|
nBits = SHIFT_BITS; // start with smallest number of bits
|
|
nStart = BREAK_POINT; // start with break pattern
|
|
|
|
for (i = 1; i < 2 * n; i += 2)
|
|
{
|
|
bitcount[table[i]] = nBits; // bitcount and pattern for next
|
|
pattern [table[i]] = nStart; // ... most frequently used byte
|
|
|
|
if (nStart-- == BREAK_POINT) // reduce pattern until at break point
|
|
{
|
|
nBits += SHIFT_BITS; // pattern increases in number of bits
|
|
nStart = (++nStart << SHIFT_BITS) - 1; // maximum value for number of bits
|
|
}
|
|
}
|
|
|
|
ValidateHeap();
|
|
|
|
pbLookup = New BYTE[nBits>>1][16]; // alloc Huffman lookup table
|
|
|
|
ValidateHeap();
|
|
|
|
for (i = 0; i < 256; i++) // setup Huffman lookup table
|
|
if (bitcount[i])
|
|
pbLookup[(bitcount[i]>>1)-1][pattern[i]] = i;
|
|
|
|
ValidateHeap();
|
|
|
|
UINT nBufSize = min(2 + (nBits << 3) + cbData, MAX_WORK_BUFFER);
|
|
|
|
pnCode = New UINT[nBufSize];
|
|
PUINT pnLoc = pnCode;
|
|
PUINT pnEnd = pnCode + nBufSize;
|
|
|
|
*pnLoc++ = nBits>>1;
|
|
memcpy(pnLoc, pbLookup, nBits<<3);
|
|
pnLoc += nBits<<1;
|
|
|
|
nBitsRemain = NUMBITS; // start with empty UINT
|
|
*pnLoc = 0; // ... and initialized
|
|
|
|
ValidateHeap();
|
|
|
|
for (i = 0; i < cbData; i++)
|
|
{
|
|
nCode = pattern [pbData[i]];
|
|
nCodeLen = bitcount[pbData[i]];
|
|
|
|
if (nBitsRemain >= nCodeLen) // room for pattern in current UINT
|
|
{
|
|
nBitsRemain -= nCodeLen;
|
|
*pnLoc |= nCode << nBitsRemain;
|
|
}
|
|
|
|
else if (nCodeLen < nBitsRemain + NUMBITS) // patterns overflows into next UINT
|
|
{
|
|
*pnLoc |= nCode >> (nCodeLen - nBitsRemain);
|
|
|
|
if (++pnLoc >= pnEnd)
|
|
{ // at end of buffer, write to disk
|
|
WriteDWords(pnCode, pnLoc - pnCode);
|
|
nWriteCount += pnLoc - pnCode;
|
|
pnLoc = pnCode;
|
|
}
|
|
|
|
nBitsRemain += NUMBITS - nCodeLen;
|
|
*pnLoc = nCode << nBitsRemain;
|
|
}
|
|
|
|
else if (nCodeLen > nBitsRemain + NUMBITS) // pattern spans next two UINT's
|
|
{
|
|
if (++pnLoc >= pnEnd - 1) // we're filling two UINT's this cycle
|
|
{
|
|
WriteDWords(pnCode, pnLoc - pnCode);
|
|
nWriteCount += pnLoc - pnCode;
|
|
pnLoc = pnCode; // at end of buffer, write to disk
|
|
}
|
|
|
|
*pnLoc++ = nCode >> (nCodeLen - nBitsRemain - NUMBITS);
|
|
nBitsRemain += (NUMBITS<<1) - nCodeLen;
|
|
*pnLoc = nCode << nBitsRemain;
|
|
}
|
|
|
|
else // pattern evenly fills next UINT
|
|
{
|
|
if (++pnLoc >= pnEnd)
|
|
{ // at end of buffer, write to disk
|
|
WriteDWords(pnCode, pnLoc - pnCode);
|
|
nWriteCount += pnLoc - pnCode;
|
|
pnLoc = pnCode;
|
|
}
|
|
|
|
nBitsRemain = 0;
|
|
*pnLoc = nCode;
|
|
}
|
|
}
|
|
|
|
ValidateHeap();
|
|
|
|
WriteDWords(pnCode, ++pnLoc - pnCode);
|
|
nWriteCount += pnLoc - pnCode;
|
|
}
|
|
__finally
|
|
{
|
|
ValidateHeap();
|
|
|
|
if (table ) { delete []table; table = NULL; }
|
|
|
|
ValidateHeap();
|
|
|
|
if (pattern ) { delete []pattern; pattern = NULL; }
|
|
|
|
ValidateHeap();
|
|
|
|
if (bitcount) { delete []bitcount; bitcount = NULL; }
|
|
|
|
ValidateHeap();
|
|
|
|
if (pbLookup) { delete []pbLookup; pbLookup = NULL; }
|
|
|
|
ValidateHeap();
|
|
|
|
if (pnCode ) { delete []pnCode; pnCode = NULL; }
|
|
}
|
|
|
|
ValidateHeap();
|
|
|
|
return nWriteCount;
|
|
}
|
|
|
|
|
|
void CPersist::WriteBytes(const BYTE *pb, UINT cb)
|
|
{
|
|
UINT cdw= (cb + sizeof(UINT) - 1) / sizeof(UINT);
|
|
|
|
for (; cdw; )
|
|
{
|
|
UINT cdwChunk= cdw;
|
|
|
|
PUINT pdw= m_pios->NextDWordsOut(&cdwChunk);
|
|
|
|
ASSERT(pdw && cdwChunk > 0);
|
|
|
|
UINT cbChunk= cdwChunk * sizeof(UINT);
|
|
|
|
if (cbChunk > cb) cbChunk= cb;
|
|
|
|
CopyMemory(pdw, pb, cbChunk);
|
|
|
|
pb += cbChunk;
|
|
cb -= cbChunk;
|
|
cdw -= cdwChunk;
|
|
}
|
|
}
|
|
|
|
UINT CPersist::SaveData(const BYTE *pbData, UINT cbData)
|
|
{
|
|
UINT off= NextOffset();
|
|
|
|
WriteBytes(pbData, cbData);
|
|
|
|
return off;
|
|
}
|
|
|
|
void CPersist::WriteWords (const WORD *pw, UINT cw )
|
|
{
|
|
UINT cWordsPerDWord= sizeof(UINT) / sizeof(USHORT);
|
|
|
|
UINT cdw= (cw + cWordsPerDWord - 1) / cWordsPerDWord;
|
|
|
|
for (; cdw; )
|
|
{
|
|
UINT cdwChunk= cdw;
|
|
|
|
PUINT pdw= m_pios->NextDWordsOut(&cdwChunk);
|
|
|
|
ASSERT(pdw && cdwChunk > 0);
|
|
|
|
UINT cwChunk= cdwChunk * cWordsPerDWord;
|
|
|
|
if (cwChunk > cw) cwChunk= cw;
|
|
|
|
CopyMemory(pdw, pw, cwChunk * sizeof(USHORT));
|
|
|
|
pw += cwChunk;
|
|
cw -= cwChunk;
|
|
cdw -= cdwChunk;
|
|
}
|
|
}
|
|
|
|
void CPersist::WriteDWords(const UINT *pdw, UINT cdw)
|
|
{
|
|
for (; cdw; )
|
|
{
|
|
UINT cdwChunk= cdw;
|
|
|
|
PUINT pdwOut= m_pios->NextDWordsOut(&cdwChunk);
|
|
|
|
ASSERT(pdwOut && cdwChunk > 0);
|
|
|
|
CopyMemory(pdwOut, pdw, cdwChunk * sizeof(UINT));
|
|
|
|
pdw += cdwChunk;
|
|
cdw -= cdwChunk;
|
|
}
|
|
}
|