]> git.pond.sub.org Git - empserver/blob - src/client/ringbuf.c
Merge branch 'readline'
[empserver] / src / client / ringbuf.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2016, 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 "ringbuf.h"
39
40 /*
41  * Initialize empty ring buffer.
42  * Not necessary if *R is already zeroed.
43  */
44 void
45 ring_init(struct ring *r)
46 {
47     r->cons = r->prod = 0;
48 }
49
50 /*
51  * Return number of bytes held in ring buffer.
52  */
53 int
54 ring_len(struct ring *r)
55 {
56     assert(r->prod - r->cons <= RING_SIZE);
57     return r->prod - r->cons;
58 }
59
60 /*
61  * Return how much space is left in ring buffer.
62  */
63 int
64 ring_space(struct ring *r)
65 {
66     return RING_SIZE - (r->prod - r->cons);
67 }
68
69 /*
70  * Peek at ring buffer contents.
71  * @n must be between -RING_SIZE-1 and RING_SIZE.
72  * If @n>=0, peek at the (@n+1)-th byte to be gotten.
73  * If @n<0, peek at the -@n-th byte that has been put in.
74  * Return the byte as unsigned char coverted to int, or EOF if there
75  * aren't that many bytes in the ring buffer.
76  */
77 int
78 ring_peek(struct ring *r, int n)
79 {
80     unsigned idx;
81
82     assert(-RING_SIZE - 1 <= n && n <= RING_SIZE);
83
84     if (n >= 0) {
85         idx = r->cons + n;
86         if (idx >= r->prod)
87             return EOF;
88     } else {
89         /* Beware, r->prod - -n can wrap around, avoid that */
90         if (r->prod < r->cons + -n)
91             return EOF;
92         idx = r->prod - -n;
93     }
94
95     return r->buf[idx % RING_SIZE];
96 }
97
98 /*
99  * Get and remove the oldest byte from the ring buffer.
100  * Return it as unsigned char converted to int, or EOF if the buffer was
101  * empty.
102  */
103 int
104 ring_getc(struct ring *r)
105 {
106     if (r->cons == r->prod)
107         return EOF;
108     return r->buf[r->cons++ % RING_SIZE];
109 }
110
111 /*
112  * Attempt to put byte @c into the ring buffer.
113  * Return EOF when the buffer is full, else @c.
114  */
115 int
116 ring_putc(struct ring *r, unsigned char c)
117 {
118     if (r->prod - r->cons == RING_SIZE)
119         return EOF;
120     return r->buf[r->prod++ % RING_SIZE] = c;
121 }
122
123 /*
124  * Attempt to put @sz bytes from @buf into the ring buffer.
125  * Return space left in ring buffer when it fits, else don't change
126  * the ring buffer and return how much space is missing times -1.
127  */
128 int
129 ring_putm(struct ring *r, void *buf, size_t sz)
130 {
131     char *p = buf;
132     int left = ring_space(r) - sz;
133     int res;
134     size_t i;
135
136     if (left >= 0) {
137         res = 0;
138         for (i = 0; i < sz; i++)
139             res = ring_putc(r, p[i]);
140         assert(res != EOF);
141     }
142
143     return left;
144 }
145
146 /*
147  * Discard the N oldest bytes from the ring buffer.
148  * It must hold at least that many.
149  */
150 void
151 ring_discard(struct ring *r, int n)
152 {
153     assert(0 <= n && n <= ring_len(r));
154     r->cons += n;
155 }
156
157 /*
158  * Search the ring buffer for zero-terminated string S.
159  * Start at the @(n+1)-th byte to be gotten.
160  * If found, return the number of bytes in the buffer before S.
161  * Else return -1.
162  */
163 int
164 ring_search(struct ring *r, char *s, int n)
165 {
166     size_t len = strlen(s);
167     size_t i, j;
168
169     for (i = r->cons + n; i + len <= r->prod; i++) {
170         for (j = 0; s[j] && s[j] == (char)r->buf[(i + j) % RING_SIZE]; j++)
171             ;
172         if (!s[j])
173             return i - r->cons;
174     }
175     return -1;
176 }
177
178 /*
179  * Fill ring buffer from file referred by file descriptor @fd.
180  * If ring buffer is already full, do nothing and return 0.
181  * Else attempt to read as many bytes as space permits, with readv(),
182  * and return its value.
183  */
184 int
185 ring_from_file(struct ring *r, int fd)
186 {
187     unsigned cons = r->cons % RING_SIZE;
188     unsigned prod = r->prod % RING_SIZE;
189     struct iovec iov[2];
190     int cnt;
191     ssize_t res;
192
193     if (r->prod == r->cons + RING_SIZE)
194         return 0;
195
196     iov[0].iov_base = r->buf + prod;
197     if (cons <= prod) {
198         /* r->buf[prod..] */
199         iov[0].iov_len = RING_SIZE - prod;
200         /* r->buf[..cons-1] */
201         iov[1].iov_base = r->buf;
202         iov[1].iov_len = cons;
203         cnt = 2;
204     } else {
205         /* r->buf[prod..cons-1] */
206         iov[0].iov_len = cons - prod;
207         cnt = 1;
208     }
209     res = readv(fd, iov, cnt);
210     if (res < 0)
211         return res;
212     r->prod += res;
213     return res;
214 }
215
216 /*
217  * Set up @iov[] to describe complete contents of ring buffer.
218  * @iov[] must have at least two elements.
219  * Return number of elements used (zero for an empty ring buffer).
220  */
221 int
222 ring_to_iovec(struct ring *r, struct iovec iov[])
223 {
224     unsigned cons = r->cons % RING_SIZE;
225     unsigned prod = r->prod % RING_SIZE;
226
227     if (r->cons == r->prod)
228         return 0;
229
230     iov[0].iov_base = r->buf + cons;
231     if (prod > cons) {
232         /* r->buf[cons..prod-1] */
233         iov[0].iov_len = prod - cons;
234         return 1;
235     }
236     /* r->buf[cons..] */
237     iov[0].iov_len = RING_SIZE - cons;
238     /* r->buf[..prod-1] */
239     iov[1].iov_base = r->buf;
240     iov[1].iov_len = prod;
241     return 2;
242 }