1 /* $Id: buffer.c,v 1.3 2009/11/17 13:58:55 steve Exp $ */
2
3 /*-
4 * Copyright (c) 2001 Steve C. Woodford.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by Steve C. Woodford.
18 * 4. The name of the author may not be used to endorse or promote products
19 * derived from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32 #include <sys/types.h>
33
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39
40 #include "buffer.h"
41
42 #ifndef MIN
43 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
44 #endif
45
46 #define BUFFER_INIT_SIZE 128
47
48 int
buffer_init_delay(struct buffer_ctx ** bcp,int fd,int txdelay)49 buffer_init_delay(struct buffer_ctx **bcp, int fd, int txdelay)
50 {
51 struct buffer_ctx *bc;
52
53 if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
54 return (-1);
55
56 if ((bc = calloc(1, sizeof(*bc))) == NULL)
57 return (-1);
58
59 if ((bc->bc_buffp = malloc(BUFFER_INIT_SIZE)) == NULL) {
60 (void) free(bc);
61 return (-1);
62 }
63
64 bc->bc_buffsize = BUFFER_INIT_SIZE;
65 bc->bc_bufflen = 0;
66 bc->bc_buffhead = 0;
67 bc->bc_bufftail = 0;
68 bc->bc_fd = fd;
69 bc->bc_txdelay = txdelay;
70
71 *bcp = bc;
72 return (0);
73 }
74
75 void
buffer_destroy(struct buffer_ctx * bc)76 buffer_destroy(struct buffer_ctx *bc)
77 {
78 (void) free(bc->bc_buffp);
79 (void) free(bc);
80 }
81
82 static ssize_t
buffer_write(struct buffer_ctx * bc,const char * src,size_t bytes)83 buffer_write(struct buffer_ctx *bc, const char *src, size_t bytes)
84 {
85
86 if (bc->bc_txdelay && bytes) {
87 usleep(bc->bc_txdelay);
88 bytes = 1;
89 }
90
91 return (write(bc->bc_fd, src, bytes));
92 }
93
94 ssize_t
buffer_fill(struct buffer_ctx * bc,const char * src,size_t bytes)95 buffer_fill(struct buffer_ctx *bc, const char *src, size_t bytes)
96 {
97 size_t copy;
98 ssize_t rv;
99
100 /*
101 * If the ring buffer is currently empty, or we manage to
102 * drain what was left, write as much of this new data as
103 * we can...
104 */
105 if (bc->bc_bufflen == 0 || (rv = buffer_drain(bc) == 0)) {
106 if ((rv = buffer_write(bc, src, bytes)) == bytes)
107 return (0);
108 if (rv > 0) {
109 bytes -= rv;
110 src = &src[rv];
111 }
112 }
113
114 if (rv < 0 && errno != EAGAIN && errno != EINTR)
115 return (rv);
116
117 /*
118 * Check if we need to grow the ring-buffer to accomodate this data...
119 */
120 if ((bc->bc_bufflen + bytes) > bc->bc_buffsize) {
121 size_t newsize;
122 char *newbuff;
123
124 for (newsize = bc->bc_buffsize * 2;
125 newsize < (bc->bc_bufflen + bytes); newsize *= 2)
126 ; /* Nothing */
127
128 if ((newbuff = malloc(newsize)) == NULL)
129 return (-1);
130
131 /* Copy the data over */
132 if (bc->bc_buffhead < bc->bc_bufftail) {
133 copy = bc->bc_buffsize - bc->bc_bufftail;
134 memcpy(newbuff, &bc->bc_buffp[bc->bc_bufftail], copy);
135 bc->bc_bufftail += copy;
136 } else
137 bc->bc_bufftail = copy = 0;
138
139 if (bc->bc_bufftail < bc->bc_buffhead) {
140 copy += (bc->bc_buffhead - bc->bc_bufftail);
141 memcpy(&newbuff[copy], &bc->bc_buffp[bc->bc_bufftail],
142 bc->bc_buffhead - bc->bc_bufftail);
143 }
144
145 bc->bc_buffhead = copy;
146 bc->bc_bufftail = 0;
147 bc->bc_buffsize = newsize;
148 (void) free(bc->bc_buffp);
149 bc->bc_buffp = newbuff;
150
151 /* Copying the new data into the buffer is trivial ... */
152 memcpy(&bc->bc_buffp[bc->bc_buffhead], src, bytes);
153 bc->bc_buffhead += bytes;
154 bc->bc_bufflen += bytes;
155 } else {
156 if (bc->bc_buffhead > bc->bc_bufftail) {
157 copy = MIN(bc->bc_buffsize - bc->bc_buffhead, bytes);
158 bytes -= copy;
159 bc->bc_bufflen += copy;
160 if (copy == 1)
161 bc->bc_buffp[bc->bc_buffhead] = *src;
162 else {
163 memcpy(&bc->bc_buffp[bc->bc_buffhead],
164 src, copy);
165 }
166 bc->bc_buffhead += copy;
167 if (bc->bc_buffhead == bc->bc_buffsize)
168 bc->bc_buffhead = 0;
169 }
170
171 if (bytes && bc->bc_buffhead < bc->bc_bufftail) {
172 copy = MIN(bc->bc_bufftail - bc->bc_buffhead, bytes);
173 bytes -= copy;
174 bc->bc_bufflen += copy;
175 if (copy == 1)
176 bc->bc_buffp[bc->bc_buffhead] = *src;
177 else {
178 memcpy(&bc->bc_buffp[bc->bc_buffhead],
179 src, copy);
180 }
181 bc->bc_buffhead += copy;
182 if (bc->bc_buffhead == bc->bc_buffsize)
183 bc->bc_buffhead = 0;
184 }
185 }
186
187 return (bc->bc_bufflen);
188 }
189
190 ssize_t
buffer_drain(struct buffer_ctx * bc)191 buffer_drain(struct buffer_ctx *bc)
192 {
193 size_t bytes;
194 ssize_t written;
195
196 if (bc->bc_buffhead < bc->bc_bufftail) {
197 bytes = bc->bc_buffsize - bc->bc_bufftail;
198 written = buffer_write(bc, &bc->bc_buffp[bc->bc_bufftail],
199 bytes);
200 if (written < bytes) {
201 if (written < 0) {
202 if (errno != EAGAIN && errno != EINTR)
203 return (-1);
204 written = 0;
205 }
206
207 bc->bc_bufftail += written;
208 } else
209 bc->bc_bufftail = 0;
210
211 bc->bc_bufflen -= written;
212 }
213
214 if (bc->bc_bufftail < bc->bc_buffhead) {
215 bytes = bc->bc_buffhead - bc->bc_bufftail;
216
217 written = buffer_write(bc, &bc->bc_buffp[bc->bc_bufftail],
218 bytes);
219 if (written < 0) {
220 if (errno != EAGAIN && errno != EINTR)
221 return (-1);
222 written = 0;
223 }
224
225 bc->bc_bufftail += written;
226 bc->bc_bufflen -= written;
227 }
228
229 return (bc->bc_bufflen);
230 }
231