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