xref: /freebsd/sbin/hastd/ebuf.c (revision e28a4053)
1 /*-
2  * Copyright (c) 2009-2010 The FreeBSD Foundation
3  * All rights reserved.
4  *
5  * This software was developed by Pawel Jakub Dawidek under sponsorship from
6  * the FreeBSD Foundation.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32 
33 #include <sys/param.h>
34 
35 #include <assert.h>
36 #include <errno.h>
37 #include <stdbool.h>
38 #include <stdint.h>
39 #include <strings.h>
40 #include <unistd.h>
41 
42 #include "ebuf.h"
43 
44 #define	EBUF_MAGIC	0xeb0f41c
45 struct ebuf {
46 	/* Magic to assert the caller uses valid structure. */
47 	int		 eb_magic;
48 	/* Address where we did the allocation. */
49 	unsigned char	*eb_start;
50 	/* Allocation end address. */
51 	unsigned char	*eb_end;
52 	/* Start of real data. */
53 	unsigned char	*eb_used;
54 	/* Size of real data. */
55 	size_t		 eb_size;
56 };
57 
58 static int ebuf_head_extend(struct ebuf *eb, size_t size);
59 static int ebuf_tail_extend(struct ebuf *eb, size_t size);
60 
61 struct ebuf *
62 ebuf_alloc(size_t size)
63 {
64 	struct ebuf *eb;
65 	int rerrno;
66 
67 	eb = malloc(sizeof(*eb));
68 	if (eb == NULL)
69 		return (NULL);
70 	size += PAGE_SIZE;
71 	eb->eb_start = malloc(size);
72 	if (eb->eb_start == NULL) {
73 		rerrno = errno;
74 		free(eb);
75 		errno = rerrno;
76 		return (NULL);
77 	}
78 	eb->eb_end = eb->eb_start + size;
79 	/*
80 	 * We set start address for real data not at the first entry, because
81 	 * we want to be able to add data at the front.
82 	 */
83 	eb->eb_used = eb->eb_start + PAGE_SIZE / 4;
84 	eb->eb_size = 0;
85 	eb->eb_magic = EBUF_MAGIC;
86 
87 	return (eb);
88 }
89 
90 void
91 ebuf_free(struct ebuf *eb)
92 {
93 
94 	assert(eb != NULL && eb->eb_magic == EBUF_MAGIC);
95 
96 	eb->eb_magic = 0;
97 
98 	free(eb->eb_start);
99 	free(eb);
100 }
101 
102 int
103 ebuf_add_head(struct ebuf *eb, const void *data, size_t size)
104 {
105 
106 	assert(eb != NULL && eb->eb_magic == EBUF_MAGIC);
107 
108 	if (size > (size_t)(eb->eb_used - eb->eb_start)) {
109 		/*
110 		 * We can't add more entries at the front, so we have to extend
111 		 * our buffer.
112 		 */
113 		if (ebuf_head_extend(eb, size) < 0)
114 			return (-1);
115 	}
116 	assert(size <= (size_t)(eb->eb_used - eb->eb_start));
117 
118 	eb->eb_size += size;
119 	eb->eb_used -= size;
120 	/*
121 	 * If data is NULL the caller just wants to reserve place.
122 	 */
123 	if (data != NULL)
124 		bcopy(data, eb->eb_used, size);
125 
126 	return (0);
127 }
128 
129 int
130 ebuf_add_tail(struct ebuf *eb, const void *data, size_t size)
131 {
132 
133 	assert(eb != NULL && eb->eb_magic == EBUF_MAGIC);
134 
135 	if (size > (size_t)(eb->eb_end - (eb->eb_used + eb->eb_size))) {
136 		/*
137 		 * We can't add more entries at the back, so we have to extend
138 		 * our buffer.
139 		 */
140 		if (ebuf_tail_extend(eb, size) < 0)
141 			return (-1);
142 	}
143 	assert(size <= (size_t)(eb->eb_end - (eb->eb_used + eb->eb_size)));
144 
145 	/*
146 	 * If data is NULL the caller just wants to reserve space.
147 	 */
148 	if (data != NULL)
149 		bcopy(data, eb->eb_used + eb->eb_size, size);
150 	eb->eb_size += size;
151 
152 	return (0);
153 }
154 
155 void
156 ebuf_del_head(struct ebuf *eb, size_t size)
157 {
158 
159 	assert(eb != NULL && eb->eb_magic == EBUF_MAGIC);
160 	assert(size <= eb->eb_size);
161 
162 	eb->eb_used += size;
163 	eb->eb_size -= size;
164 }
165 
166 void
167 ebuf_del_tail(struct ebuf *eb, size_t size)
168 {
169 
170 	assert(eb != NULL && eb->eb_magic == EBUF_MAGIC);
171 	assert(size <= eb->eb_size);
172 
173 	eb->eb_size -= size;
174 }
175 
176 /*
177  * Return pointer to the data and data size.
178  */
179 void *
180 ebuf_data(struct ebuf *eb, size_t *sizep)
181 {
182 
183 	assert(eb != NULL && eb->eb_magic == EBUF_MAGIC);
184 
185 	if (sizep != NULL)
186 		*sizep = eb->eb_size;
187 	return (eb->eb_size > 0 ? eb->eb_used : NULL);
188 }
189 
190 /*
191  * Return data size.
192  */
193 size_t
194 ebuf_size(struct ebuf *eb)
195 {
196 
197 	assert(eb != NULL && eb->eb_magic == EBUF_MAGIC);
198 
199 	return (eb->eb_size);
200 }
201 
202 /*
203  * Function adds size + (PAGE_SIZE / 4) bytes at the front of the buffer..
204  */
205 static int
206 ebuf_head_extend(struct ebuf *eb, size_t size)
207 {
208 	unsigned char *newstart, *newused;
209 	size_t newsize;
210 
211 	assert(eb != NULL && eb->eb_magic == EBUF_MAGIC);
212 
213 	newsize = eb->eb_end - eb->eb_start + (PAGE_SIZE / 4) + size;
214 
215 	newstart = malloc(newsize);
216 	if (newstart == NULL)
217 		return (-1);
218 	newused =
219 	    newstart + (PAGE_SIZE / 4) + size + (eb->eb_used - eb->eb_start);
220 
221 	bcopy(eb->eb_used, newused, eb->eb_size);
222 
223 	eb->eb_start = newstart;
224 	eb->eb_used = newused;
225 	eb->eb_end = newstart + newsize;
226 
227 	return (0);
228 }
229 
230 /*
231  * Function adds size + ((3 * PAGE_SIZE) / 4) bytes at the back.
232  */
233 static int
234 ebuf_tail_extend(struct ebuf *eb, size_t size)
235 {
236 	unsigned char *newstart;
237 	size_t newsize;
238 
239 	assert(eb != NULL && eb->eb_magic == EBUF_MAGIC);
240 
241 	newsize = eb->eb_end - eb->eb_start + size + ((3 * PAGE_SIZE) / 4);
242 
243 	newstart = realloc(eb->eb_start, newsize);
244 	if (newstart == NULL)
245 		return (-1);
246 
247 	eb->eb_used = newstart + (eb->eb_used - eb->eb_start);
248 	eb->eb_start = newstart;
249 	eb->eb_end = newstart + newsize;
250 
251 	return (0);
252 }
253