📄 cfilemap.cpp
字号:
size_t cbThisChunk = m_cbChunk;
if (m_cbStreamPos + cbThisChunk > m_cbFileSize) {
cbThisChunk = (size_t)(m_cbFileSize - m_cbStreamPos);
}
void *pView, *pUserView;
pView = MapUserView(m_hMapping, m_cbStreamPos, cbThisChunk, &pUserView, FILE_MAP_READ);
CSeg *pSeg = new CSegMap(NULL, cbThisChunk, pUserView, pView, m_cbStreamPos, true);
m_cbStreamPos += cbThisChunk;
return pSeg;
}
/// \brief Initialize member variables etc.
CSinkMemFile::CSinkMemFile() {
m_szFileName = NULL;
m_hFile = INVALID_HANDLE_VALUE;
m_hMapping = NULL;
InitializeCriticalSection(&m_CritSect);
m_cbOutPos = m_cbInPos = 0;
}
/// \brief Additional destruction necessary...
CSinkMemFile::~CSinkMemFile() {
delete[] m_szFileName;
DeleteCriticalSection(&m_CritSect);
}
/// \brief Get the handle to the opened file
/// \return An operating system handle to the open file.
HANDLE
CSinkMemFile::GetHandle() {
return m_hFile;
}
/// \brief Set file and chunk size
/// \param szFileName The name of the file, it is copied and saved here.
/// \param cbChunk The size of the chunks we provide upon request via OutGetSeg() upstream.
/// \return A pointer to 'this' CSourceMemFile
CSinkMemFile *
CSinkMemFile::Init(const TCHAR *szFileName, size_t cbChunk) {
m_cbChunk = cbChunk;
size_t cbLen = lstrlen(szFileName);
CopyMemory(m_szFileName = new _TCHAR[cbLen+1], szFileName, (cbLen + 1) * sizeof (_TCHAR));
return this;
}
/// \brief Close the file, as well as all mappings, set end of file etc.
///
/// Check for errors with GetErrorCode()
/// \return true to propagate the close.
bool
CSinkMemFile::OutClose() {
if (m_hMapping) {
do { // Once, poor mans try
if (!CloseHandle(m_hMapping)) {
SetError(ERROR_CODE_GENERIC, _T("CSinkMemFile::Close failed to close mapping [%s]"), my_ptr<_TCHAR>(AxLib::APerror()).get());
break;
}
m_hMapping = NULL;
} while (false);
}
if (m_hFile != INVALID_HANDLE_VALUE) {
do { // Once
//
// Set end of file pointer, this is determined by m_cbOutPos
//
if (SetFilePointer(m_hFile, ((LARGE_INTEGER *)&m_cbOutPos)->LowPart, &((LARGE_INTEGER *)&m_cbOutPos)->HighPart, FILE_BEGIN) == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) {
SetError(ERROR_CODE_GENERIC, _T("CSinkMemFile::Close set file pointer to end failed [%s]"), my_ptr<_TCHAR>(AxLib::APerror()).get());
break;
}
if (!SetEndOfFile(m_hFile)) {
DWORD dwErr = GetLastError();
SetError(ERROR_CODE_GENERIC, _T("CSinkMemFile::Close can't set end of file [%s]"), my_ptr<_TCHAR>(AxLib::APerror()).get());
break;
}
if (!CloseHandle(m_hFile)) {
SetError(ERROR_CODE_GENERIC, _T("CSinkMemFile::Close failed to close file [%s]"), my_ptr<_TCHAR>(AxLib::APerror()).get());
break;
}
m_hFile = INVALID_HANDLE_VALUE;
} while (false);
}
return true;
}
/// \brief Get a writeable segment, mapped to the output file if possible.
///
/// Map a request for an output segment directly to the output file. If necessary,
/// re-create a mapping to a possibly extended file.
///
/// For this to be useful, the upstream caller must get segments in the same
/// sequence that they are output, and also keep them the same size, i.e. not
/// get a segment and then change the CSeg::Len() or CSeg::Drop() bytes off it.
/// In fact using CSeg::Drop() will cause undefined effects. Shortening is
/// possible, but strongly recommended against, as it will make the code quite
/// inefficient.
/// \param cb The size in bytes of the segment to get.
/// \return A (possibly memory mapped) pointer to a CSeg of the requested size.
CSeg *
CSinkMemFile::OutGetSeg(size_t cb) {
CSeg *pSeg;
EnterCriticalSection(&m_CritSect);
// We can only get a mapped segment if we have an open file already.
longlong cbPos = m_cbInPos > m_cbOutPos ? m_cbInPos : m_cbOutPos;
if (m_hFile != INVALID_HANDLE_VALUE) {
if (cbPos + cb > m_cbMappingSize) {
m_cbMappingSize = cbPos + cb;
if (m_hMapping) {
// This actually works even if we have open views, the order
// of calls to unmapview and closehandle is not important.
if (!CloseHandle(m_hMapping)) {
SetError(ERROR_CODE_GENERIC, _T("CSinkMemFile::OutGetSeg failed to close mapping [%s]"), my_ptr<_TCHAR>(AxLib::APerror()).get());
LeaveCriticalSection(&m_CritSect);
return NULL;
}
m_hMapping = NULL;
}
}
if (!m_hMapping) {
// Now create a mapping that is large enough for the largest stream to date.
m_hMapping = CreateFileMapping(m_hFile, NULL, PAGE_READWRITE, ((LARGE_INTEGER *)&m_cbMappingSize)->HighPart, ((LARGE_INTEGER *)&m_cbMappingSize)->LowPart, NULL);
if (!m_hMapping) {
SetError(ERROR_CODE_GENERIC, _T("CSinkMemFile::OutGetSeg failed to create file mapping [%s]"), my_ptr<_TCHAR>(AxLib::APerror()).get());
LeaveCriticalSection(&m_CritSect);
return NULL;
}
}
void *pView, *pUserView;
pView = MapUserView(m_hMapping, cbPos, cb, &pUserView, FILE_MAP_WRITE|FILE_MAP_READ);
pSeg = new CSegMap(this, cb, pUserView, pView, cbPos);
} else {
pSeg = new CSeg(cb);
}
m_cbInPos = cbPos + cb;
LeaveCriticalSection(&m_CritSect);
return pSeg;
}
/// \brief Write a segment to the file, optimizing the case where it already is a mapping.
///
/// Write a segment to the file by copying into a memory mapped segment, unless
/// we're already such a segment, in which case we actually need do nothing except
/// keep track of the length of valid data 'written'.
/// \param pSeg The segment, possibly actually a CSegMap, determined by run time type info.
void
CSinkMemFile::Out(CSeg *pSeg) {
if (CSeg::IsSeg(pSeg)) {
// We need to be in a critical section, since the previous section of
// the pipe may be in a different thread and may request allocation from
// OutGetSeg().
EnterCriticalSection(&m_CritSect);
if (pSeg->Len()) {
// If this is not a CSegMap, or if it is, we're not the owner then we allocate an output.
if ((pSeg->RTClassId() != CSegMap::ClassId()) || !((CSegMap *)pSeg)->IsOwner(this)) {
CSeg *pOutSeg = OutGetSeg(pSeg->Len());
if (!pOutSeg) {
SetError(ERROR_CODE_GENERIC, _T("CSinkMemFile::Out() [OutGetSeg() returned NULL]"));
} else {
CopyMemory(pOutSeg->PtrWr(), pSeg->PtrRd(), pSeg->Len());
pOutSeg->Release();
}
} else {
// If we're 'short' of data in the output, we move the segment
// back. This can happen if an earlier requested segment was cut
// short before being output.
CSegMap *pSegMap = (CSegMap *)pSeg;
if (pSegMap->GetPos() > m_cbOutPos) {
void *pView, *pUserView;
pView = MapUserView(m_hMapping, m_cbOutPos, pSegMap->Len(), &pUserView, FILE_MAP_WRITE|FILE_MAP_READ);
if (!pView) {
SetError(ERROR_CODE_GENERIC, _T("CSinkMemFile::Out() [MapUserView() returned NULL]"));
} else {
CopyMemory(pUserView, pSegMap->PtrRd(), pSegMap->Len());
if (!(UnmapViewOfFile(pView))) {
SetError(ERROR_CODE_GENERIC, _T("CSinkMemFile::Out() [UnmapViewOfFile() failed: %s]"), my_ptr<_TCHAR>(AxLib::APerror()).get());
}
}
} else if (pSegMap->GetPos() < m_cbOutPos) {
// If we've already output into this segment, it's run over and
// we've violated the stream model. This can happen if a non-mapped
// segment is gotten, and then before it's 'output', a non-mapped
// segment is output. This is an error condition.
SetError(ERROR_CODE_GENERIC, _T("CSinkMemFile::Out() [Output sequence error]"));
}
// If (which is the normal case) the output-position is the same as the
// segment's start, everything is ok and we do nothing.
}
}
m_cbOutPos += pSeg->Len();
pSeg->Release();
LeaveCriticalSection(&m_CritSect);
}
}
/// \brief Open the file named in Init() for output
///
/// Check for errors with GetErrorCode().
/// \return true to propagate the open, which we do if no error. false is not an error condition.
bool
CSinkMemFile::OutOpen() {
// If it's there, open for writing, otherwise create it.
m_hFile = CreateFile(m_szFileName, GENERIC_WRITE|GENERIC_READ, 0, NULL, OPEN_ALWAYS, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if (m_hFile == INVALID_HANDLE_VALUE) {
SetError(ERROR_CODE_GENERIC, _T("CSinkMemFile::CSinkMemFile open error [%s]"), my_ptr<_TCHAR>(AxLib::APerror(m_szFileName)).get());
}
((LARGE_INTEGER *)&m_cbMappingSize)->LowPart = GetFileSize(m_hFile, (LPDWORD)&(((LARGE_INTEGER *)&m_cbMappingSize)->HighPart));
m_cbOutPos = m_cbInPos = 0;
return true;
}
};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -