]> git.pond.sub.org Git - empserver/blob - src/client/ringbuf.c
client: Rearrange ring_to_iovec() for clarity
[empserver] / src / client / ringbuf.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2015, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                Ken Stevens, Steve McClure, Markus Armbruster
5  *
6  *  Empire is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  *  ---
20  *
21  *  See files README, COPYING and CREDITS in the root of the source
22  *  tree for related information and legal notices.  It is expected
23  *  that future projects/authors will amend these files as needed.
24  *
25  *  ---
26  *
27  *  ringbuf.c: Simple ring buffer
28  *
29  *  Known contributors to this file:
30  *     Markus Armbruster, 2007-2017
31  */
32
33 #include <config.h>
34
35 #include <assert.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <sys/uio.h>
39 #include "ringbuf.h"
40
41 /*
42  * Initialize empty ring buffer.
43  * Not necessary if *R is already zeroed.
44  */
45 void
46 ring_init(struct ring *r)
47 {
48     r->cons = r->prod = 0;
49 }
50
51 /*
52  * Return number of bytes held in ring buffer.
53  */
54 int
55 ring_len(struct ring *r)
56 {
57     assert(r->prod - r->cons <= RING_SIZE);
58     return r->prod - r->cons;
59 }
60
61 /*
62  * Return how much space is left in ring buffer.
63  */
64 int
65 ring_space(struct ring *r)
66 {
67     return RING_SIZE - (r->prod - r->cons);
68 }
69
70 /*
71  * Peek at ring buffer contents.
72  * @n must be between -RING_SIZE-1 and RING_SIZE.
73  * If @n>=0, peek at the (@n+1)-th byte to be gotten.
74  * If @n<0, peek at the -@n-th byte that has been put in.
75  * Return the byte as unsigned char coverted to int, or EOF if there
76  * aren't that many bytes in the ring buffer.
77  */
78 int
79 ring_peek(struct ring *r, int n)
80 {
81     unsigned idx;
82
83     assert(-RING_SIZE - 1 <= n && n <= RING_SIZE);
84
85     if (n >= 0) {
86         idx = r->cons + n;
87         if (idx >= r->prod)
88             return EOF;
89     } else {
90         /* Beware, r->prod - -n can wrap around, avoid that */
91         if (r->prod < r->cons + -n)
92             return EOF;
93         idx = r->prod - -n;
94     }
95
96     return r->buf[idx % RING_SIZE];
97 }
98
99 /*
100  * Get and remove the oldest byte from the ring buffer.
101  * Return it as unsigned char converted to int, or EOF if the buffer was
102  * empty.
103  */
104 int
105 ring_getc(struct ring *r)
106 {
107     if (r->cons == r->prod)
108         return EOF;
109     return r->buf[r->cons++ % RING_SIZE];
110 }
111
112 /*
113  * Attempt to put byte @c into the ring buffer.
114  * Return EOF when the buffer is full, else @c.
115  */
116 int
117 ring_putc(struct ring *r, unsigned char c)
118 {
119     if (r->prod - r->cons == RING_SIZE)
120         return EOF;
121     return r->buf[r->prod++ % RING_SIZE] = c;
122 }
123
124 /*
125  * Attempt to put @sz bytes from @buf into the ring buffer.
126  * Return space left in ring buffer when it fits, else don't change
127  * the ring buffer and return how much space is missing times -1.
128  */
129 int
130 ring_putm(struct ring *r, void *buf, size_t sz)
131 {
132     char *p = buf;
133     int left = ring_space(r) - sz;
134     int res;
135     size_t i;
136
137     if (left >= 0) {
138         res = 0;
139         for (i = 0; i < sz; i++)
140             res = ring_putc(r, p[i]);
141         assert(res != EOF);
142     }
143
144     return left;
145 }
146
147 /*
148  * Discard the N oldest bytes from the ring buffer.
149  * It must hold at least that many.
150  */
151 void
152 ring_discard(struct ring *r, int n)
153 {
154     assert(0 <= n && n <= ring_len(r));
155     r->cons += n;
156 }
157
158 /*
159  * Search the ring buffer for zero-terminated string S.
160  * Start at the @(n+1)-th byte to be gotten.
161  * If found, return the number of bytes in the buffer before S.
162  * Else return -1.
163  */
164 int
165 ring_search(struct ring *r, char *s, int n)
166 {
167     size_t len = strlen(s);
168     size_t i, j;
169
170     for (i = r->cons + n; i + len <= r->prod; i++) {
171         for (j = 0; s[j] && s[j] == (char)r->buf[(i + j) % RING_SIZE]; j++)
172             ;
173         if (!s[j])
174             return i - r->cons;
175     }
176     return -1;
177 }
178
179 /*
180  * Fill ring buffer from file referred by file descriptor @fd.
181  * If ring buffer is already full, do nothing and return 0.
182  * Else attempt to read as many bytes as space permits, with readv(),
183  * and return its value.
184  */
185 int
186 ring_from_file(struct ring *r, int fd)
187 {
188     unsigned cons = r->cons % RING_SIZE;
189     unsigned prod = r->prod % RING_SIZE;
190     struct iovec iov[2];
191     int cnt;
192     ssize_t res;
193
194     if (r->prod == r->cons + RING_SIZE)
195         return 0;
196
197     iov[0].iov_base = r->buf + prod;
198     if (cons <= prod) {
199         /* r->buf[prod..] */
200         iov[0].iov_len = RING_SIZE - prod;
201         /* r->buf[..cons-1] */
202         iov[1].iov_base = r->buf;
203         iov[1].iov_len = cons;
204         cnt = 2;
205     } else {
206         /* r->buf[prod..cons-1] */
207         iov[0].iov_len = cons - prod;
208         cnt = 1;
209     }
210     res = readv(fd, iov, cnt);
211     if (res < 0)
212         return res;
213     r->prod += res;
214     return res;
215 }
216
217 /*
218  * Set up @iov[] to describe complete contents of ring buffer.
219  * @iov[] must have at least two elements.
220  * Return number of elements used (zero for an empty ring buffer).
221  */
222 static int
223 ring_to_iovec(struct ring *r, struct iovec iov[])
224 {
225     unsigned cons = r->cons % RING_SIZE;
226     unsigned prod = r->prod % RING_SIZE;
227
228     if (r->cons == r->prod)
229         return 0;
230
231     iov[0].iov_base = r->buf + cons;
232     if (prod > cons) {
233         /* r->buf[cons..prod-1] */
234         iov[0].iov_len = prod - cons;
235         return 1;
236     }
237     /* r->buf[cons..] */
238     iov[0].iov_len = RING_SIZE - cons;
239     /* r->buf[..prod-1] */
240     iov[1].iov_base = r->buf;
241     iov[1].iov_len = prod;
242     return 2;
243 }
244
245 /*
246  * Drain ring buffer to file referred by file descriptor @fd.
247  * If ring buffer is already empty, do nothing and return 0.
248  * Else attempt to write complete contents with writev(), and return
249  * its value.
250  */
251 int
252 ring_to_file(struct ring *r, int fd)
253 {
254     struct iovec iov[2];
255     int cnt;
256     ssize_t res;
257
258     cnt = ring_to_iovec(r, iov);
259     res = writev(fd, iov, cnt);
260     if (res < 0)
261         return res;
262     ring_discard(r, res);
263     return res;
264 }