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