fbuf: New data structure for buffered file I/O.
[pspp] / src / libpspp / fbuf.h
diff --git a/src/libpspp/fbuf.h b/src/libpspp/fbuf.h
new file mode 100644 (file)
index 0000000..94a56a2
--- /dev/null
@@ -0,0 +1,140 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef LIBPSPP_FBUF_H
+#define LIBPSPP_FBUF_H 1
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include "compiler.h"
+
+/* Data structure for buffered file I/O.
+
+   fbuf is much like stdio and serves the same purpose.  The main difference is
+   that it is defined entirely in PSPP and thus it is possible to extend it in
+   a portable way, in particular to have a portable way to read and write a
+   memory buffer instead of a file descriptor, since GNU and BSD libc provide
+   ways to do that, but other libcs don't. */
+
+struct fbuf;
+
+struct fbuf *fbuf_open_fd (int fd);
+int fbuf_open_file (const char *filename, int flags, mode_t mode,
+                    struct fbuf **) WARN_UNUSED_RESULT;
+struct fbuf *fbuf_open_memory (void *, size_t);
+
+int fbuf_close (struct fbuf *);
+
+int fbuf_get_status (const struct fbuf *);
+void fbuf_clear_status (struct fbuf *);
+
+int fbuf_is_seekable (const struct fbuf *);
+off_t fbuf_get_size (const struct fbuf *);
+
+int fbuf_flush (struct fbuf *);
+
+off_t fbuf_tell (const struct fbuf *);
+int fbuf_seek (struct fbuf *, off_t);
+
+static inline int fbuf_putc (struct fbuf *, uint8_t);
+ssize_t fbuf_write (struct fbuf *, const void *, size_t);
+
+static inline int fbuf_getc (struct fbuf *);
+ssize_t fbuf_read (struct fbuf *, void *, size_t);
+\f
+/* Implementation details. */
+
+struct fbuf
+  {
+    const struct fbuf_class *class;
+    uint8_t *buffer;
+
+    /* Offset in the underlying file descriptor:
+
+          - In read mode, this is the offset of 'read_head'.
+
+          - In write mode, this is the offset of 'write_tail'.
+
+       Starts out at TYPE_MINIMUM (off_t), which indicates that the underlying
+       descriptor offset is not known.  Negative errno values indicate
+       errors. */
+    off_t offset;
+
+    /*
+      In read mode, buffered data is read into the start of 'buffer'.
+      Initially 'read_tail' points to 'buffer' and 'read_head' just past the
+      last byte of buffered data.  As the client reads data, 'read_tail'
+      advances until it reaches 'read_head', then the buffer is re-filled and
+      the process repeats.
+
+                                      offset in fd
+                                            |
+                                            v
+        +----------+------------------------+
+        |          |  ...data to be read... |
+        +-----------------------------------+
+        ^          ^                        ^
+        |          |                        |
+      buffer  read_tail                 read_head
+
+      In write mode, read_tail and read_head are both NULL. */
+    uint8_t *read_tail, *read_head;
+
+    /*
+       In write mode, write_tail and write_head initially point to 'buffer' and
+       'write_end' to 'buffer + FBUF_SIZE'.  As the client writes, its data is
+       copied to and advances 'write_head', limited by 'write_end'.  As the
+       fbuf flushes data to the fd, 'write_tail' advances, and when
+       'write_tail' catches 'write_head', both reset to 'buffer'.
+
+             offset in fd
+                   |
+                   v
+        +----------+------------------------+-------------------+
+        |          |...data to be flushed...|                   |
+        +-----------------------------------+-------------------+
+        ^          ^                        ^                   ^
+        |          |                        |                   |
+      buffer   write_tail               write_head           write_end
+
+       In read mode, write_tail, write_head, and write_end are all NULL. */
+    uint8_t *write_tail, *write_head, *write_end;
+    int status;
+  };
+
+static inline int
+fbuf_putc (struct fbuf *fbuf, uint8_t byte)
+{
+  if (fbuf->write_head < fbuf->write_end)
+    {
+      *fbuf->write_head++ = byte;
+      return 0;
+    }
+  else
+    return fbuf_write (fbuf, &byte, 1);
+}
+
+int fbuf_getc__ (struct fbuf *);
+int
+fbuf_getc (struct fbuf *fbuf)
+{
+  return (fbuf->read_tail < fbuf->read_head
+          ? *fbuf->read_tail++
+          : fbuf_getc__ (fbuf));
+}
+
+#endif /* libpspp/fbuf.h */