]> git.pond.sub.org Git - empserver/blob - src/client/ringbuf.c
Update copyright notice
[empserver] / src / client / ringbuf.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2009, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                           Ken Stevens, Steve McClure
5  *
6  *  This program 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 2 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, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  *  ---
21  *
22  *  See files README, COPYING and CREDITS in the root of the source
23  *  tree for related information and legal notices.  It is expected
24  *  that future projects/authors will amend these files as needed.
25  *
26  *  ---
27  *
28  *  ringbuf.c: Simple ring buffer
29  *
30  *  Known contributors to this file:
31  *     Markus Armbruster, 2007
32  */
33
34 #include <config.h>
35
36 #include <assert.h>
37 #include <stdio.h>
38 #include <string.h>
39 #ifndef _WIN32
40 #include <sys/uio.h>
41 #include <unistd.h>
42 #else
43 #define readv(fd, iov, iovcnt) \
44     w32_readv_handle((fd), (iov), (iovcnt))
45 #define writev(fd, iov, iovcnt) \
46     w32_writev_socket((fd), (iov), (iovcnt))
47 #endif
48 #include "misc.h"
49 #include "ringbuf.h"
50
51 /*
52  * Initialize empty ring buffer.
53  * Not necessary if *R is already zeroed.
54  */
55 void
56 ring_init(struct ring *r)
57 {
58     r->cons = r->prod = 0;
59 }
60
61 /*
62  * Return number of bytes held in ring buffer.
63  */
64 int
65 ring_len(struct ring *r)
66 {
67     assert(r->prod - r->cons <= RING_SIZE);
68     return r->prod - r->cons;
69 }
70
71 /*
72  * Return how much space is left in ring buffer.
73  */
74 int
75 ring_space(struct ring *r)
76 {
77     return RING_SIZE - (r->prod - r->cons);
78 }
79
80 /*
81  * Peek at ring buffer contents.
82  * N must be between -RING_SIZE-1 and RING_SIZE.
83  * If N>=0, peek at the (N+1)-th byte to be gotten.
84  * If N<0, peek at the -N-th byte that has been put in.
85  * Return the byte as unsigned char coverted to int, or EOF if there
86  * aren't that many bytes in the ring buffer.
87  */
88 int
89 ring_peek(struct ring *r, int n)
90 {
91     unsigned idx;
92
93     assert(-RING_SIZE - 1 <= n && n <= RING_SIZE);
94
95     idx = n >= 0 ? r->cons + n : r->prod - -n;
96     if (idx < r->cons && idx >= r->prod)
97         return EOF;
98     return r->buf[idx % RING_SIZE];
99 }
100
101 /*
102  * Get and remove the oldest byte from the ring buffer.
103  * Return it as unsigned char coverted to int, or EOF if the buffer was
104  * empty.
105  */
106 int
107 ring_getc(struct ring *r)
108 {
109     if (r->cons == r->prod)
110         return EOF;
111     return r->buf[r->cons++ % RING_SIZE];
112 }
113
114 /*
115  * Attempt to put byte C into the ring buffer.
116  * Return EOF when the buffer is full, else C.
117  */
118 int
119 ring_putc(struct ring *r, unsigned char c)
120 {
121     if (r->prod - r->cons == RING_SIZE)
122         return EOF;
123     return r->buf[r->prod++ % RING_SIZE] = c;
124 }
125
126 /*
127  * Attempt to put SZ bytes from BUF into the ring buffer.
128  * Return space left in ring buffer when it fits, else don't change
129  * the ring buffer and return how much space is missing times -1.
130  */
131 int
132 ring_putm(struct ring *r, void *buf, size_t sz)
133 {
134     char *p = buf;
135     int left = ring_space(r) - sz;
136     int res;
137     size_t i;
138
139     if (left >= 0) {
140         res = 0;
141         for (i = 0; i < sz; i++)
142             res = ring_putc(r, p[i]);
143         assert(res != EOF);
144     }
145
146     return left;
147 }
148
149 /*
150  * Discard the N oldest bytes from the ring buffer.
151  * It must hold at least that many.
152  */
153 void
154 ring_discard(struct ring *r, int n)
155 {
156     assert(0 <= n && n <= ring_len(r));
157     r->cons += n;
158 }
159
160 /*
161  * Search the ring buffer for zero-terminated string S.
162  * If found, return a non-negative value referring to the beginning of
163  * S in the buffer when passed to ring_peek().  Else return -1.
164  */
165 int
166 ring_search(struct ring *r, char *s)
167 {
168     size_t len = strlen(s);
169     size_t i, j;
170
171     for (i = r->cons; i + len <= r->prod; i++) {
172         for (j = 0; j < len && s[j] == r->buf[(i + j) % RING_SIZE]; j++) ;
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  * Drain ring buffer to file referred by file descriptor FD.
219  * If ring buffer is already empty, do nothing and return 0.
220  * Else attempt to write complete contents with writev(), and return
221  * its value.
222  */
223 int
224 ring_to_file(struct ring *r, int fd)
225 {
226     unsigned cons = r->cons % RING_SIZE;
227     unsigned prod = r->prod % RING_SIZE;
228     struct iovec iov[2];
229     int cnt;
230     ssize_t res;
231
232     if (r->cons == r->prod)
233         return 0;
234
235     iov[0].iov_base = r->buf + cons;
236     if (prod <= cons) {
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         cnt = 2;
243     } else {
244         /* r->buf[cons..prod-1] */
245         iov[0].iov_len = prod - cons;
246         cnt = 1;
247     }
248     res = writev(fd, iov, cnt);
249     if (res < 0)
250         return res;
251     r->cons += res;
252     return res;
253 }