1 /*
2  * Copyright (c) 2006, 2008 Alexey Vatchenko <av@bsdua.org>
3  *
4  * Permission to use, copy, modify, and/or distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 #include <sys/types.h>
18 
19 #include <errno.h>
20 #include <limits.h>
21 #include <stdlib.h>
22 #include <stdint.h>
23 #include <string.h>
24 
25 #include "vpool.h"
26 
27 static void	vpool_shift(struct vpool *pool);
28 static int	vpool_new_size(struct vpool *pool, size_t datsize,
29 		    size_t *size);
30 static int	vpool_resize(struct vpool *pool, size_t datsize);
31 
32 static void
vpool_shift(struct vpool * pool)33 vpool_shift(struct vpool *pool)
34 {
35 	if (pool->v_buf != pool->v_basebuf) {
36 		memmove(pool->v_basebuf, pool->v_buf, pool->v_off);
37 		pool->v_buf = pool->v_basebuf;
38 	}
39 }
40 
41 static int
vpool_new_size(struct vpool * pool,size_t datsize,size_t * size)42 vpool_new_size(struct vpool *pool, size_t datsize, size_t *size)
43 {
44 	size_t need;
45 	size_t rem;
46 
47 	if (datsize <= pool->v_size - pool->v_off) {
48 		*size = pool->v_size;
49 		return (0);
50 	}
51 
52 	/* Check limit of new requested size */
53 	if (pool->v_limit - pool->v_off < datsize)
54 		return (EFBIG);
55 	need = pool->v_off + datsize;
56 
57 	/* Check limit of new size aligned to block size */
58 	rem = need % pool->v_blksize;
59 	if (rem != 0) {
60 		if (pool->v_limit - pool->v_off >=
61 		    datsize + (pool->v_blksize - rem))
62 			need += pool->v_blksize - rem;
63 		else
64 			need = pool->v_limit;
65 	}
66 
67 	*size = need;
68 	return (0);
69 }
70 
71 static int
vpool_resize(struct vpool * pool,size_t datsize)72 vpool_resize(struct vpool *pool, size_t datsize)
73 {
74 	char *ret;
75 	size_t size;
76 	int error;
77 
78 	error = vpool_new_size(pool, datsize, &size);
79 	if (error != 0)
80 		return (error);
81 
82 	if (size > pool->v_size) {
83 		ret = (char *) malloc(size);
84 		if (ret == NULL)
85 			return (ENOMEM);
86 
87 		memcpy(ret, pool->v_buf, pool->v_off);
88 		free(pool->v_basebuf);
89 		pool->v_basebuf = pool->v_buf = ret;
90 		pool->v_size = size;
91 	} else if ((pool->v_size - pool->v_off) -
92 	    (size_t)((char*)pool->v_buf - (char*)pool->v_basebuf) < datsize)
93 		vpool_shift(pool);
94 
95 	return (0);
96 }
97 
98 void
vpool_init(struct vpool * pool,size_t blksize,size_t limit)99 vpool_init(struct vpool *pool, size_t blksize, size_t limit)
100 {
101 
102 	pool->v_basebuf = pool->v_buf = NULL;
103 	pool->v_off = pool->v_size = 0;
104 
105 	pool->v_blksize = (blksize == 0) ? 4096 : blksize;	/* XXX */
106 	pool->v_limit = (limit == 0) ? SIZE_MAX : limit;
107 
108 	pool->v_lasterr = 0;
109 }
110 
111 void
vpool_final(struct vpool * pool)112 vpool_final(struct vpool *pool)
113 {
114 	free(pool->v_basebuf);
115 }
116 
117 void
vpool_reset(struct vpool * pool)118 vpool_reset(struct vpool *pool)
119 {
120 	free(pool->v_basebuf);
121 	pool->v_basebuf = pool->v_buf = NULL;
122 	pool->v_off = pool->v_size = 0;
123 	pool->v_lasterr = 0;
124 }
125 
126 void
vpool_wipe(struct vpool * pool)127 vpool_wipe(struct vpool *pool)
128 {
129 	pool->v_off = 0;
130 	pool->v_lasterr = 0;
131 }
132 
133 void *
vpool_insert(struct vpool * pool,size_t where,void * data,size_t datsize)134 vpool_insert(struct vpool *pool, size_t where, void *data, size_t datsize)
135 {
136 	char *ret;
137 	int error;
138 
139 	error = vpool_resize(pool, datsize);
140 	if (error != 0) {
141 		pool->v_lasterr = error;
142 		return (NULL);
143 	}
144 
145 	/*
146 	 * If ``where'' is greater than or equal to offset then
147 	 * we are appending data to the end of the buffer.
148 	 */
149 	if (where > pool->v_off)
150 		where = pool->v_off;
151 
152 	ret = (char *)pool->v_buf + where;
153 	if (pool->v_off - where > 0)
154 		memmove(ret + datsize, ret, pool->v_off - where);
155 	memcpy(ret, data, datsize);
156 	pool->v_off += datsize;
157 	pool->v_lasterr = 0;
158 
159 	return (ret);
160 }
161 
162 void *
vpool_expand(struct vpool * pool,size_t where,size_t size)163 vpool_expand(struct vpool *pool, size_t where, size_t size)
164 {
165 	char *ret;
166 	int error;
167 
168 	error = vpool_resize(pool, size);
169 	if (error != 0) {
170 		pool->v_lasterr = error;
171 		return (NULL);
172 	}
173 
174 	/*
175 	 * If ``where'' is greater than or equal to offset then
176 	 * we are appending data to the end of the buffer.
177 	 */
178 	if (where > pool->v_off)
179 		where = pool->v_off;
180 
181 	ret = (char *)pool->v_buf + where;
182 	if (pool->v_off - where > 0)
183 		memmove(ret + size, ret, pool->v_off - where);
184 	pool->v_off += size;
185 	pool->v_lasterr = 0;
186 
187 	return (ret);
188 }
189 
190 int
vpool_truncate(struct vpool * pool,size_t where,size_t size,enum vpool_trunc how)191 vpool_truncate(struct vpool *pool,
192     size_t where, size_t size, enum vpool_trunc how)
193 {
194 	/* Check if caller wants to remove more data than we have */
195 	if (where >= pool->v_off ||
196 	    size > pool->v_off || pool->v_off - size < where) {
197 		pool->v_lasterr = ERANGE;
198 		return (pool->v_lasterr);
199 	}
200 
201 	if (how == VPOOL_EXCLUDE) {
202 		if (where == 0) {
203 			/*
204 			 * Optimization.
205 			 * Don't move data, just adjust pointer.
206 			 */
207 		    pool->v_buf = (char *)pool->v_buf + size;
208 		} else {
209 			memmove((char *)pool->v_buf + where,
210 			    (char *)pool->v_buf + where + size,
211 			    pool->v_off - size - where);
212 		}
213 		pool->v_off -= size;
214 	} else {
215 		pool->v_buf = (char*)(pool->v_buf) + where;
216 		pool->v_off = size;
217 	}
218 
219 	pool->v_lasterr = 0;
220 	return (0);
221 }
222 
223 void
vpool_export(struct vpool * pool,void ** buf,size_t * size)224 vpool_export(struct vpool *pool, void **buf, size_t *size)
225 {
226 	vpool_shift(pool);
227 	*buf = pool->v_buf;
228 	*size = pool->v_off;
229 	pool->v_basebuf = pool->v_buf = NULL;
230 	pool->v_off = pool->v_size = 0;
231 	pool->v_lasterr = 0;
232 }
233