1 /*
2 FILE_FLAG_NO_BUFFERING file I/O for Windows
3 Copyright Lasse Mikkel Reinhold 2006-2009
4 GPL-1 and GPL-2 licensed.
5 
6 Only intended for use in qpress (too problem-specific design to have general usefullness).
7 
8 FILE_FLAG_NO_BUFFERING file I/O on Windows requires source address, destination address and
9 transfer size to be sector aligned.
10 
11 This library creates a read buffer (read_buffer), write buffer (write_buffer) and an aligned
12 intermediate transfer buffer (io_commit) and issues requests to the Windows API in multipla of
13 AIO_MAX_SECTOR_SIZE in size.
14 
15 The user first needs to call aio_init() with the largest transfer the user expects to take place
16 (in order to setup buffers).
17 
18 On non-Windows all this is bypassed and translates into normal fread() and fwrite(). Same when
19 aio_init() is called with buffered_io = true.
20 */
21 
22 //#define PRE_ALLOCATE
23 
24 #include "aio.hpp"
25 #include "utilities.hpp"
26 
27 #define _CRT_SECURE_NO_WARNINGS
28 
29 #include "utilities.hpp"
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <string.h>
33 
34 #ifdef WINDOWS
35     #define handle_type HANDLE
36     #include <windows.h>
37 #else
38     #define handle_type FILE *
39 #endif
40 
41 static size_t largest_request_pub;
42 static bool buffering = true;
43 
44 // read
45 #ifdef WINDOWS
46   static size_t avail;
47   static size_t src;
48 #endif
49 static handle_type ifile;
50 static char *read_buffer = 0;
51 
52 // write
53 static char *write_buffer = 0;
54 static char *io_commit = 0;
55 
56 #ifdef WINDOWS
57   static size_t queued = 0;
58 #endif
59 
60 static handle_type ofile;
61 static unsigned long long written;
62 static char destination_file[5000];
63 static unsigned long long last_extended_to = 0;
64 
aio_init(size_t largest_request,bool buffered_io)65 bool aio_init(size_t largest_request, bool buffered_io)
66 {
67     buffering = buffered_io;
68     if(buffering == false)
69     {
70         size_t allocate = largest_request + AIO_MAX_SECTOR_SIZE - 1;
71         largest_request_pub = largest_request;
72 
73         read_buffer = (char *)aligned_realloc(read_buffer, allocate, AIO_MAX_SECTOR_SIZE);
74         io_commit = (char *)aligned_realloc(io_commit, allocate, AIO_MAX_SECTOR_SIZE);
75         write_buffer = (char *)aligned_realloc(write_buffer, allocate, AIO_MAX_SECTOR_SIZE);
76 
77         if(read_buffer == 0 || io_commit == 0 || write_buffer == 0)
78         {
79             abort("Error allocating memory - decrease -T and -K flags");
80         }
81     }
82     return true;
83 }
84 
85 
FREAD(void * DstBuf,size_t Count,handle_type File)86 size_t FREAD(void *DstBuf, size_t Count, handle_type File)
87 {
88 #ifdef WINDOWS
89     size_t cn = 0;
90     DWORD read = 1;
91     size_t n = Count;
92     while(n > 0 && read != 0)
93     {
94         ReadFile(File, (char*)DstBuf + cn, (DWORD)n, &read, 0);
95         cn += read;
96         n = n - read;
97     }
98     return cn;
99 #else
100     return fread(DstBuf, 1, Count, File);
101 #endif
102 }
103 
104 
aread(void * dst,size_t size)105 size_t aread(void *dst, size_t size)
106 {
107 #ifdef WINDOWS
108     if(buffering)
109     {
110         return FREAD(dst, size, ifile);
111     }
112     else
113     {
114         char *dst2 = (char *)dst;
115         if(size > largest_request_pub)
116             abort("Internal error, aread(%d) with largest_request == %d", (int)size, (int)largest_request_pub);
117 
118         if(size > avail)
119         {
120             size_t pending = size;
121             size_t n = (pending - avail) / AIO_MAX_SECTOR_SIZE;
122             if(n * AIO_MAX_SECTOR_SIZE < pending - avail)
123                 n++;
124             size_t read = FREAD(io_commit, n*AIO_MAX_SECTOR_SIZE, ifile);
125 
126             memcpy(dst2, read_buffer, avail);
127             pending -= avail;
128             dst2 += avail;
129 
130             if(pending > read)
131             {
132                 size_t ret = read + avail;
133                 memcpy(dst2, io_commit, read);
134                 avail = 0;
135                 return ret;
136             }
137             else
138             {
139                 memcpy(dst2, io_commit, pending);
140                 memcpy(read_buffer, io_commit + pending, read - pending);
141                 avail = read - pending;
142                 return size;
143             }
144         }
145         else
146         {
147             memcpy(dst2, read_buffer, size);
148             memmove(read_buffer, read_buffer + size, avail - size);
149             avail -= size;
150             return size;
151         }
152     }
153 #else
154     return FREAD(dst, size, ifile);
155 #endif
156 }
157 
158 
aclose_read(void)159 void aclose_read(void)
160 {
161 #ifdef WINDOWS
162     CloseHandle(ifile);
163 #else
164     fclose(ifile);
165 #endif
166 }
167 
aopen_read(const char * file)168 bool aopen_read(const char *file)
169 {
170 #ifdef WINDOWS
171     if(strcmp(file, "<stdin>") == 0)
172     {
173         ifile = GetStdHandle(STD_INPUT_HANDLE);
174         return true;
175     }
176     else
177     {
178         if(buffering)
179             ifile = CreateFile(file, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN | FILE_ATTRIBUTE_NORMAL, NULL);
180         else
181             ifile = CreateFile(file, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING | FILE_FLAG_SEQUENTIAL_SCAN | FILE_ATTRIBUTE_NORMAL, NULL);
182         if (ifile != INVALID_HANDLE_VALUE)
183         {
184             src = 0;
185             avail = 0;
186             return true;
187         }
188         else
189         {
190             return false;
191         }
192     }
193 #else
194     if(strcmp(file, "<stdin>") == 0)
195     {
196         ifile = stdin;
197         return true;
198     }
199     else
200     {
201         ifile = fopen(file, "rb");
202         return (ifile != 0);
203     }
204 #endif
205 }
206 
207 
FWRITE(const void * Str,size_t Count,handle_type File)208 size_t FWRITE(const void *Str, size_t Count, handle_type File)
209 {
210 #ifdef WINDOWS
211     DWORD dummy;
212     WriteFile(File, Str, (DWORD)Count, &dummy, 0);
213     return (size_t)dummy;
214 #else
215     return fwrite(Str, 1, Count, File);
216 #endif
217 }
218 
219 
220 #ifdef WINDOWS
myFileSeek(HANDLE hf,__int64 distance,DWORD MoveMethod)221 __int64 myFileSeek (HANDLE hf, __int64 distance, DWORD MoveMethod)
222 {
223    LARGE_INTEGER li;
224    li.QuadPart = distance;
225    li.LowPart = SetFilePointer (hf, li.LowPart, &li.HighPart, MoveMethod);
226    return li.QuadPart;
227 }
228 #endif
229 
awrite(const void * src,size_t size)230 size_t awrite(const void *src, size_t size)
231 {
232 #ifdef WINDOWS
233     if(buffering)
234     {
235 #ifdef PRE_ALLOCATE
236         if(written > last_extended_to)
237         {
238             myFileSeek(ofile, written + 1000000000, FILE_BEGIN);
239             last_extended_to = written + 1000000000;
240             SetEndOfFile(ofile);
241             myFileSeek(ofile, written, FILE_BEGIN);
242         }
243 #endif
244         written += size;
245         return FWRITE(src, size, ofile);
246     }
247     else
248     {
249         size_t n;
250 
251         if(size > largest_request_pub)
252             abort("Internal error, aread(%d) with largest_request == %d", (int)size, (int)largest_request_pub);
253 
254         memcpy(write_buffer + queued, src, size);
255         queued += size;
256         written += size;
257         n = queued / AIO_MAX_SECTOR_SIZE;
258         size_t wrote = FWRITE(write_buffer, n*AIO_MAX_SECTOR_SIZE, ofile);
259         queued -= n*AIO_MAX_SECTOR_SIZE;
260         memmove(write_buffer, write_buffer + n*AIO_MAX_SECTOR_SIZE, queued);
261         if (wrote != n*AIO_MAX_SECTOR_SIZE)
262             return 0;
263         return size;
264     }
265 #else
266     written += size;
267     return FWRITE(src, size, ofile);
268 #endif
269 }
270 
271 
awritten(void)272 unsigned long long awritten(void)
273 {
274     return written;
275 }
276 
277 
aclose_write(void)278 bool aclose_write(void)
279 {
280 #ifdef WINDOWS
281     if(buffering)
282     {
283 #ifdef PRE_ALLOCATE
284         if(!CloseHandle(ofile))
285             return false;
286         ofile = CreateFile(destination_file, GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
287         if(ofile == INVALID_HANDLE_VALUE)
288             return false;
289         myFileSeek(ofile, awritten(), SEEK_CUR);
290         SetEndOfFile(ofile);
291 #endif
292         return CloseHandle(ofile) != 0;
293     }
294     else
295     {
296         if(ofile == GetStdHandle(STD_OUTPUT_HANDLE))
297         {
298             return CloseHandle(ofile) != 0;
299         }
300         else
301         {
302             // write remaining data in queue, round UP to nearest sector size (we may write
303             // too much but file will be trucnated later, below).
304             size_t n = (queued | (AIO_MAX_SECTOR_SIZE - 1)) + 1;
305             FWRITE(write_buffer, n, ofile);
306 
307             if(!CloseHandle(ofile))
308                 return false;
309             ofile = CreateFile(destination_file, GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
310             if(ofile == INVALID_HANDLE_VALUE)
311                 return false;
312             myFileSeek(ofile, awritten(), SEEK_CUR);
313             SetEndOfFile(ofile);
314             return CloseHandle(ofile) != 0;
315         }
316     }
317 #else
318 	if(ofile != 0)
319 	    fclose(ofile);
320     return true;
321 #endif
322 }
323 
324 
aopen_write(const char * file)325 bool aopen_write(const char *file)
326 {
327     strcpy(destination_file, file);
328     written = 0;
329     last_extended_to = 0;
330 
331 #ifdef WINDOWS
332     if(strcmp(file, "<stdout>") == 0)
333     {
334         ofile = GetStdHandle(STD_OUTPUT_HANDLE);
335         queued = 0;
336         return true;
337     }
338     else
339     {
340         queued = 0;
341         if(buffering)
342             ofile = CreateFile(file, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_FLAG_SEQUENTIAL_SCAN | FILE_ATTRIBUTE_NORMAL, NULL);
343         else
344             ofile = CreateFile(file, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_FLAG_NO_BUFFERING | FILE_FLAG_SEQUENTIAL_SCAN | FILE_ATTRIBUTE_NORMAL, NULL);
345         return (ofile != INVALID_HANDLE_VALUE);
346     }
347 #else
348     if(strcmp(file, "<stdout>") == 0)
349     {
350         ofile = stdout;
351         return true;
352     }
353     else
354     {
355         ofile = fopen(file, "wb");
356         return (ofile != 0);
357     }
358 #endif
359 }
360 
361 
try_awrite(const void * Str,size_t Count)362 size_t try_awrite(const void *Str, size_t Count)
363 {
364     size_t r = awrite(Str, Count);
365     if (r != Count)
366         abort("Disk full while writing destination file");
367 
368     return r;
369 }
370 
try_aread(void * DstBuf,size_t Count)371 size_t try_aread(void *DstBuf, size_t Count)
372 {
373     size_t r = aread(DstBuf, Count);
374     if (r != Count)
375         abort("Unexpected end of source file -- try the -R flag to recover");
376 
377     return r;
378 }
379 
fwrite64(unsigned long long i)380 void fwrite64(unsigned long long i)
381 {
382     unsigned long long j = i;
383     char c[8];
384     for(int k = 0; k < 8; k++)
385     {
386         c[k] = (unsigned char)j;
387         j >>= 8;
388     }
389     try_awrite(c, 8);
390 }
391 
fwrite32(unsigned int i)392 void fwrite32(unsigned int i)
393 {
394     unsigned int j = i;
395     char c[4];
396     for(int k = 0; k < 4; k++)
397     {
398         c[k] = (char)j;
399         j >>= 8;
400     }
401     try_awrite(c, 4);
402 }
403 
fread32(void)404 unsigned int fread32(void)
405 {
406     unsigned int j = 0;
407     unsigned char c[4];
408     try_aread(c, 4);
409     for(int k = 0; k < 4; k++)
410     {
411         j = j << 8;
412         j = (j | c[3 - k]);
413     }
414     return j;
415 }
416 
fread64()417 unsigned long long fread64()
418 {
419     unsigned long long j = 0;
420     unsigned char c[8];
421     try_aread(c, 8);
422 
423     for(int k = 0; k < 8; k++)
424     {
425         j = j << 8;
426         j = j | c[7 - k];
427     }
428     return j;
429 }
430 
adelete_write(void)431 void adelete_write(void)
432 {
433     aclose_write();
434 #ifdef WINDOWS
435     DeleteFile(destination_file);
436 #else
437     remove(destination_file);
438 #endif
439 }
440 
441 
442 
443