/***************************************************************************** * * * CSTREAM.CPP * * * * Copyright (C) Microsoft Corporation 1990-1997 * * All Rights reserved. * * * *****************************************************************************/ #include "header.h" #include "cstream.h" #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #ifdef _DEBUG static LONG hsemReadCount; static LONG hsemStartCount; #define PTR_SEM_READ_COUNT &hsemReadCount #define PTR_SEM_START_COUNT &hsemStartCount #else #define PTR_SEM_READ_COUNT NULL #define PTR_SEM_START_COUNT NULL #endif #ifndef SEEK_CUR #define SEEK_CUR 1 #define SEEK_END 2 #define SEEK_SET 0 #endif static HANDLE hsemRead; // semaphore used for dual-cpu processing static HANDLE hsemStart; // semaphore used for dual-cpu processing static CStream *pThis; static BOOL fReadAheadStarted; static BOOL fExitThread; static PBYTE pbuf1; // first buffer for dual-cpu processing static PBYTE pbuf2; // second buffer for dual-cpu processing /////////////////////// CStream implementation //////////////////////////// // We use our own stream class instead of using the C runtime, because this // change alone doubled the speed of the help compiler. I.e., the C runtime // implementation of stream io is horribly slow. 26-Feb-1994 [ralphw] // Also, this CStream will take advantage of a dual-CPU and will do // read-aheads in a thread. CStream::CStream(PCSTR pszFileName) { // Only one CStream can use the read-ahead thread if (!pThis) { fDualCPU = g_fDualCPU==-1?FALSE:g_fDualCPU; pThis = this; } else fDualCPU = FALSE; if ((hfile = _lopen(pszFileName, OF_READ)) == HFILE_ERROR) { fInitialized = FALSE; if (fDualCPU) pThis = NULL; return; } if (fDualCPU) { cbBuf = DUAL_CSTREAM_BUF_SIZE; if (!hsemRead) { hsemRead = CreateSemaphore(NULL, 1, 1, NULL); hsemStart = CreateSemaphore(NULL, 1, 1, NULL); pbuf2 = (PBYTE) lcMalloc(DUAL_CSTREAM_BUF_SIZE + 2); pbuf1 = (PBYTE) lcMalloc(DUAL_CSTREAM_BUF_SIZE + 2); } pbuf = pbuf1; } else { cbBuf = CSTREAM_BUF_SIZE; // +2 because we add a zero just past the buffer in case anyone expects strings pbuf = (PBYTE) lcMalloc(CSTREAM_BUF_SIZE + 2); } ASSERT(pbuf); fInitialized = TRUE; int cread = _lread(hfile, pbuf, cbBuf); if (cread == HFILE_ERROR) { _lclose(hfile); fInitialized = FALSE; if (fDualCPU) pThis = NULL; return; } if (fDualCPU) { // Start reading the next buffer cThrdRead = HFILE_NOTREAD; if (!fReadAheadStarted) { hthrd = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) &ReadAhead, NULL, 0, &idThrd); ASSERT(hthrd); if (!hthrd) fDualCPU = FALSE; else fReadAheadStarted = TRUE; } else { ReleaseSemaphore(hsemRead, 1, PTR_SEM_READ_COUNT); ReleaseSemaphore(hsemStart, 1, PTR_SEM_START_COUNT); // start read-ahead thread } } pCurBuf = pbuf; pEndBuf = pbuf + cread; pEndBuf[1] = '\0'; lFilePos = cread; lFileBuf = 0; pszFile = lcStrDup(pszFileName); fInitialized = TRUE; m_fEndOfFile = FALSE; } CStream::~CStream() { if (fInitialized) { if (fDualCPU) { Cleanup(); WaitForReadAhead(); pThis = NULL; } else { lcFree(pbuf); } lcFree(pszFile); _lclose(hfile); } } /*************************************************************************** FUNCTION: CStream::ReadBuf PURPOSE: Read the next block into buffer PARAMETERS: void RETURNS: COMMENTS: Zero-terminates the buffer MODIFICATION DATES: 13-Nov-1994 [ralphw] ***************************************************************************/ char CStream::ReadBuf(void) { int cread; if (fDualCPU) { ASSERT(fReadAheadStarted); WaitForReadAhead(); if (pbuf == pbuf1) pbuf = pbuf2; else pbuf = pbuf1; cread = cThrdRead; cThrdRead = HFILE_NOTREAD; ReleaseSemaphore(hsemRead, 1, PTR_SEM_READ_COUNT); // Error-checking must occur AFTER we release the read semaphore if (cread == HFILE_ERROR) { return chEOF; } ReleaseSemaphore(hsemStart, 1, PTR_SEM_START_COUNT); // start read-ahead thread } else { cread = _lread(hfile, pbuf, cbBuf); if (cread == HFILE_ERROR) { return chEOF; } } lFileBuf = lFilePos; lFilePos += cread; pCurBuf = pbuf; pEndBuf = pbuf + cread; pEndBuf[1] = '\0'; if (!cread) { m_fEndOfFile = TRUE; return chEOF; } return (char) *pCurBuf++; } UINT STDCALL CStream::read(void* pbDest, int cbBytes) { if (pEndBuf - pbuf < cbBuf) if (pEndBuf - pCurBuf < cbBytes) cbBytes = (int)(pEndBuf - pCurBuf); if (pCurBuf + cbBytes < pEndBuf) { memcpy(pbDest, pCurBuf, cbBytes); pCurBuf += cbBytes; return cbBytes; } PBYTE pbDst = (PBYTE) pbDest; // If destination buffer is larger then our internal buffer, then // recursively call until we have filled up the destination. int cbRead = (int)(pEndBuf - pCurBuf); memcpy(pbDest, pCurBuf, cbRead); pbDst += cbRead; ReadBuf(); if (m_fEndOfFile) return cbRead; else pCurBuf--; // since ReadBuf incremented it return read(pbDst, cbBytes - cbRead) + cbRead; } int STDCALL CStream::seek(int pos, SEEK_TYPE seek) { ASSERT(seek != SK_END); // we don't support this one if (seek == SK_CUR) pos = lFileBuf + (int)(pCurBuf - pbuf) + pos; if (pos >= lFileBuf && pos < lFilePos) { pCurBuf = pbuf + (pos - lFileBuf); if (pCurBuf >= pEndBuf && pEndBuf < pbuf + cbBuf) m_fEndOfFile = TRUE; return lFileBuf + (int)(pCurBuf - pbuf); } else { if (fDualCPU) { WaitForReadAhead(); } lFileBuf = _llseek(hfile, pos, SEEK_SET); int cread = _lread(hfile, pbuf, cbBuf); if (cread == HFILE_ERROR) { m_fEndOfFile = TRUE; return chEOF; } lFilePos = lFileBuf + cread; pCurBuf = pbuf; pEndBuf = pbuf + cread; if (fDualCPU) { cThrdRead = HFILE_NOTREAD; ReleaseSemaphore(hsemRead, 1, PTR_SEM_READ_COUNT); if (fReadAheadStarted) ReleaseSemaphore(hsemStart, 1, PTR_SEM_START_COUNT); // start read-ahead thread } if (cread == 0) m_fEndOfFile = TRUE; return lFilePos; } } void CStream::WaitForReadAhead(void) { for(;;) { WaitForSingleObject(hsemRead, INFINITE); if (cThrdRead == HFILE_NOTREAD) { ReleaseSemaphore(hsemRead, 1, PTR_SEM_READ_COUNT); Sleep(1); // give read-ahead thread a chance to run } else return; } } /*************************************************************************** FUNCTION: ReadAhead PURPOSE: Reads the next block of data from a file PARAMETERS: pthis RETURNS: COMMENTS: Two semaphores control this thread: hsemStart: keeps the thread suspended while waiting for the caller to finish reading one of its blocks. hsemRead: used as a signal between the main thread and this thread as to when this thread has completed. Because it is theoretically possible for a thread switch to occur between the time the hsemStart thread starts this thread and this thread's call to WaitForSingleObject(hsemRead), the caller also sets the read return value to HFILE_NOTREAD to ensure that it does not attempt to use this buffer until in fact the read has completed. NOTE: this thread approach only makes sense on a system with more then one CPU. MODIFICATION DATES: 05-Feb-1997 [ralphw] ***************************************************************************/ DWORD WINAPI ReadAhead(LPVOID pv) { /* * Each time through the loop, we block on hsemStart, waiting for our * caller to release it. The hsemRead is used to block the caller until * we have completed our read. */ for (;;) { PBYTE pbReadBuf; WaitForSingleObject(hsemStart, INFINITE); if (fExitThread) break; WaitForSingleObject(hsemRead, INFINITE); pbReadBuf = (pThis->pbuf == pbuf1) ? pbuf2 : pbuf1; pThis->cThrdRead = _lread(pThis->hfile, pbReadBuf, pThis->cbBuf); ReleaseSemaphore(hsemRead, 1, PTR_SEM_READ_COUNT); } ExitThread(0); return 0; } /*************************************************************************** FUNCTION: CStream::Cleanup PURPOSE: Cleanup global variables PARAMETERS: void RETURNS: COMMENTS: MODIFICATION DATES: 05-Feb-1997 [ralphw] ***************************************************************************/ void CStream::Cleanup(void) { if (fReadAheadStarted) { fExitThread = TRUE; ReleaseSemaphore(hsemStart, 1, PTR_SEM_START_COUNT); // start read-ahead thread Sleep(1); // Let the other thread run CloseHandle(hsemStart); CloseHandle(hsemRead); lcFree(pbuf1); lcFree(pbuf2); hsemStart = hsemRead = pbuf1 = pbuf2 = NULL; } } #ifdef _DEBUG char CStream::cget() { if (pCurBuf < pEndBuf) return (char) *pCurBuf++; else if (pEndBuf < pbuf + cbBuf) { m_fEndOfFile = TRUE; return chEOF; } else return ReadBuf(); } #endif