I use a sliding window in one of my applications (in fact, several layers of sliding windows work on top of each other, but this is beyond the scope of this discussion). The window uses the view of the file with memory mapping via CreateFileMapping() and MapViewOfFile() , then I have an abstraction layer on top of this. I set the abstraction level for any range of bytes that I need, and this ensures that file matching and file browsing are adjusted accordingly so that these bytes are in memory. Each time a new range of bytes is requested, a file view is only configured if necessary.
The view of the file is positioned and scaled at the borders of the pages, which are even a multiple of the granularity of the system, as GetSystemInfo() reports. Just because a scan reaches the end of a given range of bytes does not necessarily mean that it has reached the boundary of the end of the page, so for the next scan, you may not need to change the appearance of the file at all, the next bytes are already in memory. If the first requested byte of the range exceeds the right border of the displayed page, the left edge of the file view is adjusted to the left border of the requested page, and all pages on the left are not displayed. If the last requested byte in the range exceeds the right border of the most displayed page, a new page is displayed and added to the file view.
This sounds more complicated than it really is to implement it after encoding it:
Creating a view in a file
It looks like you are scanning bytes in fixed-size blocks, so this approach is very fast and very efficient for this. Based on this method, I can sequentially scan files with several GIGBYTEs from start to finish pretty quickly, usually a minute or less on my slowest machine. If your files are smaller than the system dimension or even a few megabytes, you are unlikely to notice that some time has passed (unless your scans are slow).
Update : here is a simplified variation of what I'm using:
class FileView { private: DWORD m_AllocGran; DWORD m_PageSize; HANDLE m_File; unsigned __int64 m_FileSize; HANDLE m_Map; unsigned __int64 m_MapSize; LPBYTE m_View; unsigned __int64 m_ViewOffset; DWORD m_ViewSize; void CloseMap() { CloseView(); if (m_Map != NULL) { CloseHandle(m_Map); m_Map = NULL; } m_MapSize = 0; } void CloseView() { if (m_View != NULL) { UnmapViewOfFile(m_View); m_View = NULL; } m_ViewOffset = 0; m_ViewSize = 0; } bool EnsureMap(unsigned __int64 Size) { // do not exceed EOF or else the file on disk will grow! Size = min(Size, m_FileSize); if ((m_Map == NULL) || (m_MapSize != Size)) { // a new map is needed... CloseMap(); ULARGE_INTEGER ul; ul.QuadPart = Size; m_Map = CreateFileMapping(m_File, NULL, PAGE_READONLY, ul.HighPart, ul.LowPart, NULL); if (m_Map == NULL) return false; m_MapSize = Size; } return true; } bool EnsureView(unsigned __int64 Offset, DWORD Size) { if ((m_View == NULL) || (Offset < m_ViewOffset) || ((Offset + Size) > (m_ViewOffset + m_ViewSize))) { // the requested range is not already in view... // round down the offset to the nearest allocation boundary unsigned __int64 ulNewOffset = ((Offset / m_AllocGran) * m_AllocGran); // round up the size to the next page boundary DWORD dwNewSize = ((((Offset - ulNewOffset) + Size) + (m_PageSize-1)) & ~(m_PageSize-1)); // if the new view will exceed EOF, truncate it unsigned __int64 ulOffsetInFile = (ulNewOffset + dwNewSize); if (ulOffsetInFile > m_FileSize) dwNewViewSize -= (ulOffsetInFile - m_FileSize); if ((m_View == NULL) || (m_ViewOffset != ulNewOffset) || (m_ViewSize != ulNewSize)) { // a new view is needed... CloseView(); // make sure the memory map is large enough to contain the entire view if (!EnsureMap(ulNewOffset + dwNewSize)) return false; ULARGE_INTEGER ul; ul.QuadPart = ulNewOffset; m_View = (LPBYTE) MapViewOfFile(m_Map, FILE_MAP_READ, ul.HighPart, ul.LowPart, dwNewSize); if (m_View == NULL) return false; m_ViewOffset = ulNewOffset; m_ViewSize = dwNewSize; } } return true; } public: FileView() : m_AllocGran(0), m_PageSize(0), m_File(INVALID_HANDLE_VALUE), m_FileSize(0), m_Map(NULL), m_MapSize(0), m_View(NULL), m_ViewOffset(0), m_ViewSize(0) { // map views need to be positioned on even multiples // of the system allocation granularity. let size // them on even multiples of the system page size... SYSTEM_INFO si = {0}; if (GetSystemInfo(&si)) { m_AllocGran = si.dwAllocationGranularity; m_PageSize = si.dwPageSize; } } ~FileView() { CloseFile(); } bool OpenFile(LPTSTR FileName) { CloseFile(); if ((m_AllocGran == 0) || (m_PageSize == 0)) return false; HANDLE hFile = CreateFile(FileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if (hFile == INVALID_HANDLE_VALUE) return false; ULARGE_INTEGER ul; ul.LowPart = GetFileSize(hFile, &ul.HighPart); if ((ul.LowPart == INVALID_FILE_SIZE) && (GetLastError() != 0)) { CloseHandle(hFile); return false; } m_File = hFile; m_FileSize = ul.QuadPart; return true; } void CloseFile() { CloseMap(); if (m_File != INVALID_HANDLE_VALUE) { CloseHandle(m_File); m_File = INVALID_HANDLE_VALUE; } m_FileSize = 0; } bool AccessBytes(unsigned __int64 Offset, DWORD Size, LPBYTE *Bytes, DWORD *Available) { if (Bytes) *Bytes = NULL; if (Available) *Available = 0; if ((m_FileSize != 0) && (offset < m_FileSize)) { // make sure the requested range is in view if (!EnsureView(Offset, Size)) return false; // near EOF, the available bytes may be less than requested DWORD dwOffsetInView = (Offset - m_ViewOffset); if (Bytes) *Bytes = &m_View[dwOffsetInView]; if (Available) *Available = min(m_ViewSize - dwOffsetInView, Size); } return true; } };
.
FileView fv; if (fv.OpenFile(TEXT("C:\\path\\file.ext"))) { LPBYTE data; DWORD len; unsigned __int64 offset = 0, filesize = fv.FileSize(); while (offset < filesize) { if (!fv.AccessBytes(offset, some size here, &data, &len)) break; // error if (len == 0) break; // unexpected EOF // use data up to len bytes as needed... offset += len; } fv.CloseFile(); }
This code is designed to randomly jump anywhere in the file for any data size. Since you read bytes sequentially, some of them can be simplified as needed.