Example: File Conversion with Extended I/O
Program 14-3, atouEX, reimplements Program 14-1. These programs show how the two asynchronous I/O techniques differ. atouEX is similar to Program 14-1 but moves most of the bookkeeping code to the completion routines, and many variables are made global so as to be accessible to the completion routines. Appendix C shows, however, that atouEX performs competitively with other non-memory-mapped techniques, whereas atouOV is consistently slower.
Program 14-2. atouEX: File Conversion with Extended I/O
/* Chapter 14. atouEX
EXTENDED I/O ASCII to Unicode file conversion. */
/* atouEX file1 file2 */
#include "EvryThng.h"
#define MAX_OVRLP 4
#define REC_SIZE 8096 /* Block size is not as important for
performance as with atouOV. */
#define UREC_SIZE 2 * REC_SIZE
static VOID WINAPI ReadDone (DWORD, DWORD, LPOVERLAPPED);
static VOID WINAPI WriteDone (DWORD, DWORD, LPOVERLAPPED);
/* The first overlapped structure is for reading,
and the second is for writing. Structures and buffers are
allocated for each outstanding operation. */
OVERLAPPED OverLapIn [MAX_OVRLP], OverLapOut [MAX_OVRLP];
CHAR AsRec [MAX_OVRLP] [REC_SIZE];
WCHAR UnRec [MAX_OVRLP] [REC_SIZE];
HANDLE hInputFile, hOutputFile;
LONGLONG nRecord, nDone;
LARGE_INTEGER FileSize;
int _tmain (int argc, LPTSTR argv [])
{
DWORD ic;
LARGE_INTEGER CurPosIn;
hInputFile = CreateFile (argv [1], GENERIC_READ,
0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
hOutputFile = CreateFile (argv [2], GENERIC_WRITE,
0, NULL, CREATE_ALWAYS, FILE_FLAG_OVERLAPPED, NULL);
FileSize.LowPart = GetFileSize (hInputFile, &FileSize.HighPart);
nRecord = FileSize.QuadPart / REC_SIZE;
if ((FileSize.QuadPart % REC_SIZE) != 0) nRecord++;
CurPosIn.QuadPart = 0;
for (ic = 0; ic < MAX_OVRLP; ic++) {
OverLapIn [ic].hEvent = (HANDLE) ic; /* Overload the event. */
OverLapOut [ic].hEvent = (HANDLE) ic; /* Fields. */
OverLapIn [ic].Offset = CurPosIn.LowPart;
OverLapIn [ic].OffsetHigh = CurPosIn.HighPart;
if (CurPosIn.QuadPart < FileSize.QuadPart)
ReadFileEx (hInputFile, AsRec [ic], REC_SIZE,
&OverLapIn [ic], ReadDone);
CurPosIn.QuadPart += (LONGLONG) REC_SIZE;
}
/* All read operations are running. Enter an alertable wait
state and continue until all records have been processed. */
nDone = 0;
while (nDone < 2 * nRecord)
SleepEx (INFINITE, TRUE);
CloseHandle (hInputFile);
CloseHandle (hOutputFile);
_tprintf (_T ("ASCII to Unicode conversion completed.\n"));
return 0;
}
static VOID WINAPI ReadDone (DWORD Code, DWORD nBytes,
LPOVERLAPPED pOv)
{
/* A read completed. Convert the data and initiate a write. */
LARGE_INTEGER CurPosIn, CurPosOut;
DWORD ic, i;
nDone++;
/* Process the record and initiate the write. */
ic = (DWORD) (pOv->hEvent);
CurPosIn.LowPart = OverLapIn [ic].Offset;
CurPosIn.HighPart = OverLapIn [ic].OffsetHigh;
CurPosOut.QuadPart =
(CurPosIn.QuadPart / REC_SIZE) * UREC_SIZE;
OverLapOut [ic].Offset = CurPosOut.LowPart;
OverLapOut [ic].OffsetHigh = CurPosOut.HighPart;
/* Convert an ASCII record to Unicode. */
for (i = 0; i < nBytes; i++)
UnRec [ic] [i] = AsRec [ic] [i];
WriteFileEx (hOutputFile, UnRec [ic], nBytes*2,
&OverLapOut [ic], WriteDone);
/* Prepare the overlapped structure for the next read. */
CurPosIn.QuadPart += REC_SIZE * (LONGLONG) (MAX_OVRLP);
OverLapIn [ic].Offset = CurPosIn.LowPart;
OverLapIn [ic].OffsetHigh = CurPosIn.HighPart;
return;
}
static VOID WINAPI WriteDone (DWORD Code, DWORD nBytes,
LPOVERLAPPED pOv)
{
/* A write completed. Initiate the next read. */
LARGE_INTEGER CurPosIn;
DWORD ic;
nDone++;
ic = (DWORD) (pOv->hEvent);
CurPosIn.LowPart = OverLapIn [ic].Offset;
CurPosIn.HighPart = OverLapIn [ic].OffsetHigh;
if (CurPosIn.QuadPart < FileSize.QuadPart) {
ReadFileEx (hInputFile, AsRec [ic], REC_SIZE,
&OverLapIn [ic], ReadDone);
}
return;
}