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