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