Another advantage of memory mapping is the ability to use convenient memory-based algorithms to process files. Sorting data in memory, for instance, is much easier than sorting records in a file.
Program 5-1 in that it assumes an 8-byte sort key at the start of the record, but it is restricted to fixed records. Program 5-5 will rectify this shortcoming, but at the cost of increased complexity.
The sorting is performed by the <stdlib.h> C library function qsort. Notice that qsort requires a programmer-defined record comparison function, which is the same as the KeyCompare function in Program 5-2.
This program structure is straightforward. Simply create the file mapping on a temporary copy of the input file, create a single view of the file, and invoke qsort. There is no file I/O. Then the sorted file is sent to standard output using _tprintf, although a null character is appended to the file map.
/* Chapter 5. sortFL. File sorting. Fixed-length records. */ /* Usage: sortFL file */ #include "EvryThng.h" typedef struct _RECORD { TCHAR Key [KEY_SIZE]; TCHAR Data [DATALEN]; } RECORD; #define RECSIZE sizeof (RECORD) int _tmain (int argc, LPTSTR argv []) { HANDLE hFile = INVALID_HANDLE_VALUE, hMap = NULL; LPVOID pFile = NULL; DWORD FsLow, Result = 2; TCHAR TempFile [MAX_PATH]; LPTSTR pTFile; /* Create the name for a temporary file to hold a copy of the file to be sorted. Sorting is done in the temp file. */ /* Alternatively, retain the file as a permanent sorted version. */ _stprintf (TempFile, _T ("%s%s"), argv [1], _T (".tmp")); CopyFile (argv [1], TempFile, TRUE); Result = 1; /* Temp file is new and should be deleted. */ /* Map the temporary file and sort it in memory. */ hFile = CreateFile (TempFile, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); FsLow = GetFileSize (hFile, NULL); hMap = CreateFileMapping (hFile, NULL, PAGE_READWRITE, 0, FsLow + TSIZE, NULL); pFile = MapViewOfFile (hMap, FILE_MAP_ALL_ACCESS, 0, 0 /* FsLow + TSIZE */, 0); qsort (pFile, FsLow / RECSIZE, RECSIZE, KeyCompare); /* KeyCompare is as in Program 52. */ /* Print the sorted file. */ pTFile = (LPTSTR) pFile; pTFile [FsLow/TSIZE] = '\0'; _tprintf (_T ("%s"), pFile); UnmapViewOfFile (pFile); CloseHandle (hMap); CloseHandle (hFile); DeleteFile (TempFile); return 0; }
This implementation is straightforward, but there is an alternative that does not require mapping. Just allocate memory, read the complete file, sort it in memory, and write it. Such a solution, included on the book's Web site, would be as effective as Appendix C.
File maps are convenient, as the preceding examples demonstrate. Suppose, however, that the program creates a data structure with pointers in a mapped file and expects to access that file in the future. Pointers will all be relative to the virtual address returned from MapViewOfFile, and they will be meaningless when mapping the file the next time. The solution is to use based pointers, which are actually offsets relative to another pointer. The Microsoft C syntax, available in Visual C++ and some other systems, is:
type _based (base) declarator
Here are two examples.
LPTSTR pInFile = NULL; DWORD _based (pInFile) *pSize; TCHAR _based (pInFile) *pIn;
Notice that the syntax forces use of the *, a practice that is contrary to Windows convention.