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