fbuf: New data structure for buffered file I/O.
[pspp] / src / libpspp / fbuf.h
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2017 Free Software Foundation, Inc.
3
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.
8
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.
13
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/>. */
16
17 #ifndef LIBPSPP_FBUF_H
18 #define LIBPSPP_FBUF_H 1
19
20 #include <stdbool.h>
21 #include <stdint.h>
22 #include <sys/types.h>
23 #include "compiler.h"
24
25 /* Data structure for buffered file I/O.
26
27    fbuf is much like stdio and serves the same purpose.  The main difference is
28    that it is defined entirely in PSPP and thus it is possible to extend it in
29    a portable way, in particular to have a portable way to read and write a
30    memory buffer instead of a file descriptor, since GNU and BSD libc provide
31    ways to do that, but other libcs don't. */
32
33 struct fbuf;
34
35 struct fbuf *fbuf_open_fd (int fd);
36 int fbuf_open_file (const char *filename, int flags, mode_t mode,
37                     struct fbuf **) WARN_UNUSED_RESULT;
38 struct fbuf *fbuf_open_memory (void *, size_t);
39
40 int fbuf_close (struct fbuf *);
41
42 int fbuf_get_status (const struct fbuf *);
43 void fbuf_clear_status (struct fbuf *);
44
45 int fbuf_is_seekable (const struct fbuf *);
46 off_t fbuf_get_size (const struct fbuf *);
47
48 int fbuf_flush (struct fbuf *);
49
50 off_t fbuf_tell (const struct fbuf *);
51 int fbuf_seek (struct fbuf *, off_t);
52
53 static inline int fbuf_putc (struct fbuf *, uint8_t);
54 ssize_t fbuf_write (struct fbuf *, const void *, size_t);
55
56 static inline int fbuf_getc (struct fbuf *);
57 ssize_t fbuf_read (struct fbuf *, void *, size_t);
58 \f
59 /* Implementation details. */
60
61 struct fbuf
62   {
63     const struct fbuf_class *class;
64     uint8_t *buffer;
65
66     /* Offset in the underlying file descriptor:
67
68           - In read mode, this is the offset of 'read_head'.
69
70           - In write mode, this is the offset of 'write_tail'.
71
72        Starts out at TYPE_MINIMUM (off_t), which indicates that the underlying
73        descriptor offset is not known.  Negative errno values indicate
74        errors. */
75     off_t offset;
76
77     /*
78       In read mode, buffered data is read into the start of 'buffer'.
79       Initially 'read_tail' points to 'buffer' and 'read_head' just past the
80       last byte of buffered data.  As the client reads data, 'read_tail'
81       advances until it reaches 'read_head', then the buffer is re-filled and
82       the process repeats.
83
84                                       offset in fd
85                                             |
86                                             v
87         +----------+------------------------+
88         |          |  ...data to be read... |
89         +-----------------------------------+
90         ^          ^                        ^
91         |          |                        |
92       buffer  read_tail                 read_head
93
94       In write mode, read_tail and read_head are both NULL. */
95     uint8_t *read_tail, *read_head;
96
97     /*
98        In write mode, write_tail and write_head initially point to 'buffer' and
99        'write_end' to 'buffer + FBUF_SIZE'.  As the client writes, its data is
100        copied to and advances 'write_head', limited by 'write_end'.  As the
101        fbuf flushes data to the fd, 'write_tail' advances, and when
102        'write_tail' catches 'write_head', both reset to 'buffer'.
103
104              offset in fd
105                    |
106                    v
107         +----------+------------------------+-------------------+
108         |          |...data to be flushed...|                   |
109         +-----------------------------------+-------------------+
110         ^          ^                        ^                   ^
111         |          |                        |                   |
112       buffer   write_tail               write_head           write_end
113
114        In read mode, write_tail, write_head, and write_end are all NULL. */
115     uint8_t *write_tail, *write_head, *write_end;
116     int status;
117   };
118
119 static inline int
120 fbuf_putc (struct fbuf *fbuf, uint8_t byte)
121 {
122   if (fbuf->write_head < fbuf->write_end)
123     {
124       *fbuf->write_head++ = byte;
125       return 0;
126     }
127   else
128     return fbuf_write (fbuf, &byte, 1);
129 }
130
131 int fbuf_getc__ (struct fbuf *);
132 int
133 fbuf_getc (struct fbuf *fbuf)
134 {
135   return (fbuf->read_tail < fbuf->read_head
136           ? *fbuf->read_tail++
137           : fbuf_getc__ (fbuf));
138 }
139
140 #endif /* libpspp/fbuf.h */