Lely core libraries 2.3.2
frbuf.c
Go to the documentation of this file.
1
24#if !_WIN32
25// This needs to be defined before any files are included to make fstat64(),
26// lseek64(), mmap64() and pread64() available.
27#define _LARGEFILE64_SOURCE 1
28#endif
29
30#include "util.h"
31
32#if !LELY_NO_STDIO
33
34#include <lely/util/errnum.h>
35#include <lely/util/frbuf.h>
36
37#include <assert.h>
38#include <stdlib.h>
39
40#if !_WIN32
41#include <stdio.h>
42#if _POSIX_C_SOURCE >= 200112L
43#include <fcntl.h>
44#if _POSIX_MAPPED_FILES >= 200112L
45#include <sys/mman.h>
46#endif
47#include <sys/stat.h>
48#endif
49#endif
50
52struct __frbuf {
53#if _WIN32
55 HANDLE hFile;
57 HANDLE hFileMappingObject;
59 LPVOID lpBaseAddress;
60#elif _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
62 int fd;
64 void *addr;
66 size_t len;
67#else
69 FILE *stream;
71 void *map;
72#endif
73};
74
75void *
76__frbuf_alloc(void)
77{
78 void *ptr = malloc(sizeof(struct __frbuf));
79 if (!ptr)
80 set_errc(errno2c(errno));
81 return ptr;
82}
83
84void
85__frbuf_free(void *ptr)
86{
87 free(ptr);
88}
89
90struct __frbuf *
91__frbuf_init(struct __frbuf *buf, const char *filename)
92{
93 assert(buf);
94 assert(filename);
95
96#if _WIN32
97 buf->hFile = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, NULL,
98 OPEN_EXISTING,
99 FILE_ATTRIBUTE_READONLY | FILE_FLAG_NO_BUFFERING, NULL);
100 if (buf->hFile == INVALID_HANDLE_VALUE)
101 return NULL;
102
103 buf->hFileMappingObject = INVALID_HANDLE_VALUE;
104 buf->lpBaseAddress = NULL;
105#elif _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
106 buf->fd = open(filename, O_RDONLY | O_CLOEXEC);
107 if (buf->fd == -1)
108 return NULL;
109
110 buf->addr = MAP_FAILED;
111 buf->len = 0;
112#else
113 buf->stream = fopen(filename, "rb");
114 if (!buf->stream)
115 return NULL;
116#endif
117
118 return buf;
119}
120
121void
122__frbuf_fini(struct __frbuf *buf)
123{
124 frbuf_unmap(buf);
125
126#if _WIN32
127 CloseHandle(buf->hFile);
128#elif _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
129 close(buf->fd);
130#else
131 fclose(buf->stream);
132#endif
133}
134
135frbuf_t *
136frbuf_create(const char *filename)
137{
138 int errc = 0;
139
140 frbuf_t *buf = __frbuf_alloc();
141 if (!buf) {
142 errc = get_errc();
143 goto error_alloc_buf;
144 }
145
146 if (!__frbuf_init(buf, filename)) {
147 errc = get_errc();
148 goto error_init_buf;
149 }
150
151 return buf;
152
153error_init_buf:
154 __frbuf_free(buf);
155error_alloc_buf:
156 set_errc(errc);
157 return NULL;
158}
159
160void
162{
163 if (buf) {
164 __frbuf_fini(buf);
165 __frbuf_free(buf);
166 }
167}
168
169intmax_t
171{
172 assert(buf);
173
174#if _WIN32
175 LARGE_INTEGER FileSize;
176 if (!GetFileSizeEx(buf->hFile, &FileSize))
177 return -1;
178 return FileSize.QuadPart;
179#elif _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
180#ifdef __linux__
181 struct stat64 stat;
182 if (fstat64(buf->fd, &stat) == -1)
183 return -1;
184#else
185 struct stat stat;
186 if (fstat(buf->fd, &stat) == -1)
187 return -1;
188#endif
189 return stat.st_size;
190#else
191 long offset = ftell(buf->stream);
192 if (offset == -1) {
193 set_errc(errno2c(errno));
194 return -1;
195 }
196
197 // WARNING: This is not guaranteed to work, but there exists no standard
198 // C alternative.
199 if (fseek(buf->stream, 0, SEEK_END)) {
200 set_errc(errno2c(errno));
201 return -1;
202 }
203
204 long size = ftell(buf->stream);
205
206 int errc = get_errc();
207 fseek(buf->stream, offset, SEEK_SET);
208 set_errc(errc);
209
210 return size;
211#endif
212}
213
214intmax_t
216{
217 assert(buf);
218
219#if _WIN32
220 LARGE_INTEGER li = { .QuadPart = 0 };
221 if (!SetFilePointerEx(buf->hFile, li, &li, FILE_CURRENT))
222 return -1;
223 return li.QuadPart;
224#elif _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
225#ifdef __linux__
226 return lseek64(buf->fd, 0, SEEK_CUR);
227#else
228 return lseek(buf->fd, 0, SEEK_CUR);
229#endif
230#else
231 long pos = ftell(buf->stream);
232 if (pos == -1)
233 set_errc(errno2c(errno));
234 return pos;
235#endif
236}
237
238intmax_t
239frbuf_set_pos(frbuf_t *buf, intmax_t pos)
240{
241 assert(buf);
242
243 if (pos < 0) {
245 return -1;
246 }
247
248#if _WIN32
249 LARGE_INTEGER li = { .QuadPart = pos };
250 if (!SetFilePointerEx(buf->hFile, li, &li, FILE_BEGIN))
251 return -1;
252 return li.QuadPart;
253#elif _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
254#ifdef __linux__
255 return lseek64(buf->fd, pos, SEEK_SET);
256#else
257 return lseek(buf->fd, pos, SEEK_SET);
258#endif
259#else
260 if (pos > LONG_MAX) {
262 return -1;
263 }
264 if (fseek(buf->stream, pos, SEEK_SET)) {
265 set_errc(errno2c(errno));
266 return -1;
267 }
268 return frbuf_get_pos(buf);
269#endif
270}
271
273frbuf_read(frbuf_t *buf, void *ptr, size_t size)
274{
275 assert(buf);
276 assert(ptr || !size);
277
278 if (!size)
279 return 0;
280
281#if _WIN32
282 DWORD nNumberOfBytesRead;
283 if (!ReadFile(buf->hFile, ptr, size, &nNumberOfBytesRead, NULL))
284 return -1;
285 return nNumberOfBytesRead;
286#elif _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
287 ssize_t result;
288 do
289 result = read(buf->fd, ptr, size);
290 while (result == -1 && errno == EINTR);
291 return result;
292#else
293 size_t result = fread(ptr, 1, size, buf->stream);
294 if (result != size && ferror(buf->stream)) {
295 set_errc(errno2c(errno));
296 if (!result)
297 return -1;
298 }
299 return result;
300#endif
301}
302
304frbuf_pread(frbuf_t *buf, void *ptr, size_t size, intmax_t pos)
305{
306 assert(buf);
307 assert(ptr || !size);
308
309 if (!size)
310 return 0;
311
312 if (pos < 0) {
314 return -1;
315 }
316
317#if _WIN32
318 ssize_t result = 0;
319 DWORD dwErrCode = GetLastError();
320
321 intmax_t oldpos = frbuf_get_pos(buf);
322 if (oldpos == -1) {
323 result = -1;
324 dwErrCode = GetLastError();
325 goto error_get_pos;
326 }
327
328 DWORD nNumberOfBytesRead;
329 OVERLAPPED Overlapped = { 0 };
330 ULARGE_INTEGER uli = { .QuadPart = pos };
331 Overlapped.Offset = uli.LowPart;
332 Overlapped.OffsetHigh = uli.HighPart;
333 // clang-format off
334 if (!ReadFile(buf->hFile, ptr, size, &nNumberOfBytesRead,
335 &Overlapped)) {
336 // clang-format on
337 result = -1;
338 dwErrCode = GetLastError();
339 goto error_ReadFile;
340 }
341
342 result = nNumberOfBytesRead;
343
344error_ReadFile:
345 if (frbuf_set_pos(buf, oldpos) == -1 && !dwErrCode)
346 dwErrCode = GetLastError();
347error_get_pos:
348 SetLastError(dwErrCode);
349 return result;
350#elif _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
351 ssize_t result;
352#ifdef __linux__
353 do
354 result = pread64(buf->fd, ptr, size, pos);
355#else
356 do
357 result = pread(buf->fd, ptr, size, pos);
358#endif
359 while (result == -1 && errno == EINTR);
360 return result;
361#else
362 ssize_t result = 0;
363 int errc = get_errc();
364
365 intmax_t oldpos = frbuf_get_pos(buf);
366 if (oldpos == -1) {
367 result = -1;
368 errc = get_errc();
369 goto error_get_pos;
370 }
371
372 if (frbuf_set_pos(buf, pos) != pos) {
373 result = -1;
374 errc = get_errc();
375 goto error_set_pos;
376 }
377
378 result = frbuf_read(buf, ptr, size);
379 if (result == -1 || (size_t)result != size)
380 errc = get_errc();
381
382error_set_pos:
383 if (frbuf_set_pos(buf, oldpos) == -1 && !errc)
384 errc = get_errc();
385error_get_pos:
386 set_errc(errc);
387 return result;
388#endif
389}
390
391const void *
392frbuf_map(frbuf_t *buf, intmax_t pos, size_t *psize)
393{
394 frbuf_unmap(buf);
395
396 intmax_t size = frbuf_get_size(buf);
397 if (size < 0)
398 return NULL;
399 if (pos < 0) {
401 return NULL;
402 }
403 if (pos > (intmax_t)size) {
405 return NULL;
406 }
407 size -= pos;
408
409 if (psize && *psize)
410 size = MIN((uintmax_t)size, *psize);
411
412#if _WIN32
413 DWORD dwErrCode = 0;
414
415 SYSTEM_INFO SystemInfo;
416 // cppcheck-suppress uninitvar
417 GetSystemInfo(&SystemInfo);
418 DWORD off = pos % SystemInfo.dwAllocationGranularity;
419 if ((uintmax_t)size > (uintmax_t)(SIZE_MAX - off)) {
420 dwErrCode = ERROR_INVALID_PARAMETER;
421 goto error_size;
422 }
423
424 ULARGE_INTEGER MaximumSize = { .QuadPart = pos + size };
425 buf->hFileMappingObject = CreateFileMapping(buf->hFile, NULL,
426 PAGE_READONLY, MaximumSize.HighPart,
427 MaximumSize.LowPart, NULL);
428 if (buf->hFileMappingObject == INVALID_HANDLE_VALUE) {
429 dwErrCode = GetLastError();
430 goto error_CreateFileMapping;
431 }
432
433 ULARGE_INTEGER FileOffset = { .QuadPart = pos - off };
434 buf->lpBaseAddress = MapViewOfFile(buf->hFileMappingObject,
435 FILE_MAP_READ, FileOffset.HighPart, FileOffset.LowPart,
436 (SIZE_T)(off + size));
437 if (!buf->lpBaseAddress) {
438 dwErrCode = GetLastError();
439 goto error_MapViewOfFile;
440 }
441
442 if (psize)
443 *psize = (size_t)size;
444
445 return (char *)buf->lpBaseAddress + off;
446
447error_MapViewOfFile:
448 CloseHandle(buf->hFileMappingObject);
449 buf->hFileMappingObject = INVALID_HANDLE_VALUE;
450error_CreateFileMapping:
451error_size:
452 SetLastError(dwErrCode);
453 return NULL;
454#elif _POSIX_MAPPED_FILES >= 200112L
455 long page_size = sysconf(_SC_PAGE_SIZE);
456 if (page_size <= 0)
457 return NULL;
458 intmax_t off = pos % page_size;
459 if ((uintmax_t)size > (uintmax_t)(SIZE_MAX - off)) {
460 errno = EOVERFLOW;
461 return NULL;
462 }
463
464#ifdef __linux__
465 buf->addr = mmap64(NULL, off + size, PROT_READ, MAP_SHARED, buf->fd,
466 pos - off);
467#else
468 // TODO: Check if `pos - off` does not overflow the range of off_t.
469 buf->addr = mmap(NULL, off + size, PROT_READ, MAP_SHARED, buf->fd,
470 pos - off);
471#endif
472 if (buf->addr == MAP_FAILED)
473 return NULL;
474 buf->len = off + size;
475
476 if (psize)
477 *psize = size;
478
479 return (char *)buf->addr + off;
480#else
481 int errc = get_errc();
482
483 buf->map = malloc(size);
484 if (!buf->map) {
485 errc = errno2c(errno);
486 goto error_malloc_map;
487 }
488
489 ssize_t result = frbuf_pread(buf, buf->map, size, pos);
490 if (result == -1) {
491 errc = get_errc();
492 goto error_pread;
493 }
494 size = result;
495
496 if (psize)
497 *psize = size;
498
499 return buf->map;
500
501error_pread:
502 free(buf->map);
503 buf->map = NULL;
504error_malloc_map:
505 set_errc(errc);
506 return NULL;
507#endif
508}
509
510int
512{
513 assert(buf);
514
515 int result = 0;
516
517#if _WIN32
518 if (buf->hFileMappingObject != INVALID_HANDLE_VALUE) {
519 DWORD dwErrCode = GetLastError();
520 if (!UnmapViewOfFile(buf->lpBaseAddress)) {
521 result = -1;
522 dwErrCode = GetLastError();
523 }
524 if (!CloseHandle(buf->hFileMappingObject) && !result) {
525 result = -1;
526 dwErrCode = GetLastError();
527 }
528 SetLastError(dwErrCode);
529
530 buf->hFileMappingObject = INVALID_HANDLE_VALUE;
531 buf->lpBaseAddress = NULL;
532 }
533#elif _POSIX_MAPPED_FILES >= 200112L
534 if (buf->addr != MAP_FAILED) {
535 result = munmap(buf->addr, buf->len);
536
537 buf->addr = MAP_FAILED;
538 buf->len = 0;
539 }
540#else
541 if (buf->map) {
542 free(buf->map);
543
544 buf->map = NULL;
545 }
546#endif
547
548 return result;
549}
550
551#endif // !LELY_NO_STDIO
This header file is part of the utilities library; it contains the native and platform-independent er...
@ ERRNUM_INVAL
Invalid argument.
Definition: errnum.h:132
@ ERRNUM_OVERFLOW
Value too large to be stored in data type.
Definition: errnum.h:204
int get_errc(void)
Returns the last (thread-specific) native error code set by a system call or library function.
Definition: errnum.c:932
void set_errc(int errc)
Sets the current (thread-specific) native error code to errc.
Definition: errnum.c:944
int errno2c(int errnum)
Transforms a standard C error number to a native error code.
Definition: errnum.c:46
void set_errnum(errnum_t errnum)
Sets the current (thread-specific) platform-independent error number to errnum.
Definition: errnum.h:424
int frbuf_unmap(frbuf_t *buf)
Unmaps the current memory map of a read file buffer, if it exists.
Definition: frbuf.c:511
intmax_t frbuf_get_size(frbuf_t *buf)
Returns the size (in bytes) of the a read file buffer, or -1 on error.
Definition: frbuf.c:170
const void * frbuf_map(frbuf_t *buf, intmax_t pos, size_t *psize)
Maps (part of) the contents of a read file buffer to memory.
Definition: frbuf.c:392
intmax_t frbuf_set_pos(frbuf_t *buf, intmax_t pos)
Sets the current offset (in bytes) of a read file buffer with respect to the beginning of the file.
Definition: frbuf.c:239
void frbuf_destroy(frbuf_t *buf)
Destroys a read file buffer.
Definition: frbuf.c:161
intmax_t frbuf_get_pos(frbuf_t *buf)
Returns the current offset (in bytes) of a read file buffer with respect to the beginning of the file...
Definition: frbuf.c:215
ssize_t frbuf_pread(frbuf_t *buf, void *ptr, size_t size, intmax_t pos)
Reads bytes from the specified position in a read file buffer.
Definition: frbuf.c:304
frbuf_t * frbuf_create(const char *filename)
Creates a new read file buffer.
Definition: frbuf.c:136
ssize_t frbuf_read(frbuf_t *buf, void *ptr, size_t size)
Reads bytes from the current position in a read file buffer.
Definition: frbuf.c:273
This header file is part of the utilities library; it contains the read file buffer declarations.
#define MIN(a, b)
Returns the minimum of a and b.
Definition: util.h:57
This is the internal header file of the utilities library.
This header file is part of the C11 and POSIX compatibility library; it includes <stdio....
This header file is part of the C11 and POSIX compatibility library; it includes <stdlib....
An read file buffer struct.
Definition: frbuf.c:52
int fd
The file descriptor.
Definition: frbuf.c:62
void * addr
The base address of the current file mapping.
Definition: frbuf.c:64
size_t len
The length (in bytes) of the mapping at addr.
Definition: frbuf.c:66
ptrdiff_t ssize_t
Used for a count of bytes or an error indication.
Definition: types.h:43