1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2017 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
31 #include "libpspp/assertion.h"
32 #include "libpspp/cast.h"
34 #include "gl/intprops.h"
35 #include "gl/minmax.h"
36 #include "gl/xalloc.h"
39 #define FBUF_SIZE 4096
43 int (*close) (struct fbuf *);
45 /* Reads up to N bytes from FBUF's underlying file descriptor into BUFFER.
46 Returns the number of bytes read, if successful, zero at end of file, or
47 a negative errno value on error. */
48 int (*read) (struct fbuf *fbuf, void *buffer, size_t n);
50 /* Writes the N bytes in BUFFER to FBUF's underlying file descriptor. The
51 * caller guarantees N > 0. Returns the number of bytes written, if
52 * successful, otherwise a negative errno value. */
53 int (*write) (struct fbuf *fbuf, const void *buffer, size_t n);
55 /* Seeks to byte offset OFFSET in FBUF's underlying file descriptor.
56 Returns 0 if successful, otherwise a positive errno value. Returns
57 -ESPIPE if FBUF does not support positioning. */
58 int (*seek) (struct fbuf *fbuf, off_t offset);
60 /* Returns the current byte offset in FBUF's underlying file descriptor, or
61 a negative errno value on error. Returns -ESPIPE
62 if FBUF does not support positioning. */
63 off_t (*tell) (struct fbuf *fbuf);
65 /* Returns the size of the file underlying FBUF, in bytes, or a negative
66 errno value on error. Returns -ESPIPE if FBUF does not support
68 off_t (*get_size) (struct fbuf *fbuf);
78 fbuf_init (struct fbuf *fbuf, const struct fbuf_class *class, off_t offset)
80 memset (fbuf, 0, sizeof *fbuf);
82 fbuf->buffer = xmalloc (FBUF_SIZE);
83 fbuf->offset = offset >= 0 ? offset : TYPE_MINIMUM (off_t);
86 /* Closes FBUF. Returns 0 if successful, otherwise a positive errno value that
87 represents an error reading or writing the underlying fd (which could have
88 happened earlier or as part of the final flush implied by closing). */
90 fbuf_close (struct fbuf *fbuf)
96 int status = fbuf->status;
97 int error = fbuf->class->close (fbuf);
98 return status ? status : error;
101 /* Returns FBUF's error status, which is 0 if no error has been recorded and
102 otherwise a positive errno value. The error, if any, reflects difficulty
103 reading or writing the underlying fd. */
105 fbuf_get_status (const struct fbuf *fbuf)
110 /* Clears any previously recorded error status. */
112 fbuf_clear_status (struct fbuf *fbuf)
117 /* Returns the length of the file backing FBUF, in bytes, or a negative errno
118 value on error. A return value of -ESPIPE indicates that the underlying
119 file is not seekable, i.e. does not have a length. */
121 fbuf_get_size (const struct fbuf *fbuf_)
123 struct fbuf *fbuf = CONST_CAST (struct fbuf *, fbuf_);
124 return fbuf->class->get_size (fbuf);
127 /* Returns true if FBUF is seekable, false otherwise. */
129 fbuf_is_seekable (const struct fbuf *fbuf)
131 return fbuf_tell (fbuf) != -ESPIPE;
134 /* Attempts to flush any data buffered for writing to the underlying file.
135 Returns 0 if successful (which includes the case where FBUF is not in write
136 mode) or a positive errno value if there is a write error. */
138 fbuf_flush (struct fbuf *fbuf)
142 assert (fbuf->write_tail <= fbuf->write_head);
143 int n = fbuf->write_head - fbuf->write_tail;
147 int retval = fbuf->class->write (fbuf, fbuf->write_tail, n);
150 fbuf->status = -retval;
154 fbuf->write_tail += n;
155 if (fbuf->offset >= 0)
157 if (fbuf->write_tail >= fbuf->write_head)
159 fbuf->write_tail = fbuf->write_head = fbuf->buffer;
165 /* Returns the byte offset in FBUF's file of the read byte to be read or
166 written, or a negative errno value if the offset cannot be determined.
167 Returns -ESPIPE if the underlying file is not seekable. */
169 fbuf_tell (const struct fbuf *fbuf_)
171 struct fbuf *fbuf = CONST_CAST (struct fbuf *, fbuf_);
173 if (fbuf->offset < 0)
175 if (fbuf->offset != -ESPIPE)
176 fbuf->offset = fbuf->class->tell (fbuf);
178 if (fbuf->offset < 0)
183 - (fbuf->read_head - fbuf->read_tail)
184 + (fbuf->write_head - fbuf->write_tail));
187 /* Attempts to seek in FBUF such that the next byte to be read or written will
188 be at byte offset OFFSET. Returns 0 if successful or a negative errno value
189 otherwise. Returns -ESPIPE if the underlying file is not seekable. */
191 fbuf_seek (struct fbuf *fbuf, off_t offset)
196 int error = fbuf_flush (fbuf);
200 fbuf->read_tail = fbuf->read_head = NULL;
201 fbuf->write_tail = fbuf->write_head = fbuf->write_end = NULL;
203 error = fbuf->class->seek (fbuf, offset);
205 fbuf->offset = offset;
209 /* Attempts to write the SIZE bytes of data in DATA to FBUF. On success,
210 returns the number of bytes actually written (possibly less than SIZE), and
211 on failure returns a negative errno value. Returns 0 only if SIZE is 0.
213 If the last I/O operation on FBUF was a read, the caller must call
214 fbuf_seek() before this function. */
216 fbuf_write (struct fbuf *fbuf, const void *data_, size_t size)
218 const uint8_t *data = data_;
219 size_t n_written = 0;
222 size_t avail = fbuf->write_end - fbuf->write_head;
223 size_t chunk = MIN (avail, size);
226 if (chunk < FBUF_SIZE)
228 /* Normal case: copy into buffer. */
229 memcpy (fbuf->write_head, data, chunk);
230 fbuf->write_head += chunk;
234 /* Buffer is empty and we're writing more data than will fit in
235 the buffer. Skip the buffer. */
236 chunk = MIN (INT_MAX, size);
237 int retval = fbuf->class->write (fbuf, data, chunk);
239 return n_written ? n_written : -retval;
240 if (fbuf->offset >= 0)
241 fbuf->offset += retval;
249 int error = fbuf_flush (fbuf);
251 return n_written ? n_written : -error;
253 /* Use fbuf_seek() to switch between reading and writing. */
254 assert (!fbuf->read_head);
256 if (!fbuf->write_tail)
258 fbuf->write_tail = fbuf->write_head = fbuf->buffer;
259 fbuf->write_end = fbuf->buffer + FBUF_SIZE;
267 fbuf_getc__ (struct fbuf *fbuf)
270 int retval = fbuf_read (fbuf, &c, 1);
271 return retval == 1 ? c : EOF;
274 /* Attempts to read SIZE bytes of data from FBUF into DATA. On success,
275 returns the number of bytes actually read (possibly less than SIZE), and on
276 failure returns a negative errno value. Returns 0 only if end of file was
277 reached before any data could be read.
279 If the last I/O operation on FBUF was a write, the caller must call
280 fbuf_seek() before this function. */
282 fbuf_read (struct fbuf *fbuf, void *data_, size_t size)
284 uint8_t *data = data_;
288 size_t avail = fbuf->read_head - fbuf->read_tail;
289 size_t chunk = MIN (avail, size);
292 /* Copy out of buffer. */
293 memcpy (data, fbuf->read_tail, chunk);
294 fbuf->read_tail += chunk;
301 /* Buffer is empty. */
303 /* Use fbuf_seek() to switch between reading and writing. */
304 assert (!fbuf->write_head);
306 if (size < FBUF_SIZE)
308 /* Normal case: fill the buffer. */
309 int retval = fbuf->class->read (fbuf, fbuf->buffer, FBUF_SIZE);
312 fbuf->status = -retval;
313 return n_read ? n_read : retval;
315 else if (retval == 0)
317 if (fbuf->offset >= 0)
318 fbuf->offset += retval;
319 fbuf->read_tail = fbuf->buffer;
320 fbuf->read_head = fbuf->buffer + retval;
324 /* Caller's read buffer is bigger than FBUF_SIZE. Use it
326 int retval = fbuf->class->read (fbuf, data, size);
329 fbuf->status = -retval;
330 return n_read ? n_read : retval;
332 else if (retval == 0)
334 if (fbuf->offset >= 0)
335 fbuf->offset += retval;
345 /* Implementation of file-based fbuf. */
347 static const struct fbuf_class fbuf_fd_class;
349 /* Returns a new fbuf that represents FD. */
351 fbuf_open_fd (int fd)
353 struct fbuf_fd *fbuf = xmalloc (sizeof *fbuf);
354 fbuf_init (&fbuf->up, &fbuf_fd_class, -1);
359 /* Opens FILENAME with FLAGS and MODE and stores a new fbuf that represents it
360 into *FBUFP. Returns 0 on success, or a positive errno value on failure.
361 ON failure, *FBUFP will be NULL. */
363 fbuf_open_file (const char *filename, int flags, mode_t mode,
366 int fd = open (filename, flags, mode);
372 *fbufp = fbuf_open_fd (fd);
376 static struct fbuf_fd *
377 fbuf_fd_cast (const struct fbuf *fbuf)
379 assert (fbuf->class == &fbuf_fd_class);
380 return UP_CAST (fbuf, struct fbuf_fd, up);
384 fbuf_fd_close (struct fbuf *fbuf_)
386 struct fbuf_fd *fbuf = fbuf_fd_cast (fbuf_);
387 int retval = close (fbuf->fd) == EOF ? errno : 0;
393 fbuf_fd_read (struct fbuf *fbuf_, void *buffer, size_t n)
395 struct fbuf_fd *fbuf = fbuf_fd_cast (fbuf_);
396 int retval = read (fbuf->fd, buffer, n);
397 return retval >= 0 ? retval : -errno;
401 fbuf_fd_write (struct fbuf *fbuf_, const void *buffer, size_t n)
403 struct fbuf_fd *fbuf = fbuf_fd_cast (fbuf_);
404 int retval = write (fbuf->fd, buffer, n);
405 return retval > 0 ? retval : -errno;
409 fbuf_fd_seek (struct fbuf *fbuf_, off_t offset)
411 struct fbuf_fd *fbuf = fbuf_fd_cast (fbuf_);
412 return lseek (fbuf->fd, offset, SEEK_SET) < 0 ? errno : 0;
416 fbuf_fd_tell (struct fbuf *fbuf_)
418 struct fbuf_fd *fbuf = fbuf_fd_cast (fbuf_);
419 off_t offset = lseek (fbuf->fd, 0, SEEK_CUR);
420 return offset >= 0 ? offset : -errno;
424 fbuf_fd_get_size (struct fbuf *fbuf_)
426 struct fbuf_fd *fbuf = fbuf_fd_cast (fbuf_);
427 off_t offset = lseek (fbuf->fd, 0, SEEK_END);
428 return offset >= 0 ? offset : -errno;
431 static const struct fbuf_class fbuf_fd_class =
445 size_t size, allocated;
448 static const struct fbuf_class fbuf_memory_class;
450 /* Takes ownership of the N bytes of data at DATA, which must have been
451 allocated with malloc(), as a memory buffer and makes it the backing for the
452 newly returned fbuf. Initially, the fbuf is positioned at the beginning of
453 the data, so that reads will read from it and writes will overwrite it. (To
454 append, use fbuf_seek() to seek to the end.)
456 Writes beyond the end will reallocate the buffer. Closing the returned fbuf
457 will free the buffer. */
459 fbuf_open_memory (void *data, size_t n)
461 struct fbuf_memory *fbuf = xmalloc (sizeof *fbuf);
462 fbuf_init (&fbuf->up, &fbuf_memory_class, 0);
469 static struct fbuf_memory *
470 fbuf_memory_cast (const struct fbuf *fbuf)
472 assert (fbuf->class == &fbuf_memory_class);
473 return UP_CAST (fbuf, struct fbuf_memory, up);
477 fbuf_memory_close (struct fbuf *fbuf_)
479 struct fbuf_memory *fbuf = fbuf_memory_cast (fbuf_);
486 fbuf_memory_read (struct fbuf *fbuf_, void *buffer, size_t n)
488 struct fbuf_memory *fbuf = fbuf_memory_cast (fbuf_);
489 if (fbuf->up.offset >= fbuf->size)
492 size_t chunk = MIN (n, fbuf->size - fbuf->up.offset);
493 memcpy (buffer, fbuf->data + fbuf->up.offset, chunk);
498 fbuf_memory_write (struct fbuf *fbuf_, const void *buffer, size_t n)
500 struct fbuf_memory *fbuf = fbuf_memory_cast (fbuf_);
502 /* Fail if write would cause the memory block to exceed SIZE_MAX bytes. */
503 size_t end = xsum (fbuf->up.offset, n);
504 if (size_overflow_p (end))
507 /* Expand fbuf->data if necessary to hold the write. */
508 if (end > fbuf->allocated)
510 fbuf->allocated = end < SIZE_MAX / 2 ? end * 2 : end;
511 fbuf->data = xrealloc (fbuf->data, fbuf->allocated);
514 /* Zero-pad to reach the current offset (although this is necessary only if
515 there has been a seek past the end), then copy in the new data. */
516 if (fbuf->up.offset > fbuf->size)
517 memset (fbuf->data + fbuf->size, 0, fbuf->up.offset - fbuf->size);
518 memcpy (fbuf->data + fbuf->up.offset, buffer, n);
520 if (end > fbuf->size)
527 fbuf_memory_seek (struct fbuf *fbuf UNUSED, off_t offset UNUSED)
533 fbuf_memory_tell (struct fbuf *fbuf UNUSED)
539 fbuf_memory_get_size (struct fbuf *fbuf_)
541 struct fbuf_memory *fbuf = fbuf_memory_cast (fbuf_);
545 static const struct fbuf_class fbuf_memory_class =
552 fbuf_memory_get_size,