Lely core libraries  2.2.5
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 
49 struct __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 
72 void *
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 
81 void
82 __frbuf_free(void *ptr)
83 {
84  free(ptr);
85 }
86 
87 struct __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 
118 void
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 
132 frbuf_t *
133 frbuf_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 
150 error_init_buf:
151  __frbuf_free(buf);
152 error_alloc_buf:
153  set_errc(errc);
154  return NULL;
155 }
156 
157 void
159 {
160  if (buf) {
161  __frbuf_fini(buf);
162  __frbuf_free(buf);
163  }
164 }
165 
166 intmax_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 
211 intmax_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 
235 intmax_t
236 frbuf_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 
269 ssize_t
270 frbuf_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 
300 ssize_t
301 frbuf_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 
341 error_ReadFile:
342  if (frbuf_set_pos(buf, oldpos) == -1 && !dwErrCode)
343  dwErrCode = GetLastError();
344 error_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 
379 error_set_pos:
380  if (frbuf_set_pos(buf, oldpos) == -1 && !errc)
381  errc = get_errc();
382 error_get_pos:
383  set_errc(errc);
384  return result;
385 #endif
386 }
387 
388 const void *
389 frbuf_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 
443 error_MapViewOfFile:
444  CloseHandle(buf->hFileMappingObject);
445  buf->hFileMappingObject = INVALID_HANDLE_VALUE;
446 error_CreateFileMapping:
447 error_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 
497 error_pread:
498  free(buf->map);
499  buf->map = NULL;
500 error_malloc_map:
501  set_errc(errc);
502  return NULL;
503 #endif
504 }
505 
506 int
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
frbuf_t * frbuf_create(const char *filename)
Creates a new read file buffer.
Definition: frbuf.c:133
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
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
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
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