/* * Copyright (c) 2006, 2008 Alexey Vatchenko * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "contrib/vpool/vpool.h" static void vpool_shift(struct vpool *pool); static int vpool_new_size(struct vpool *pool, size_t datsize, size_t *size); static int vpool_resize(struct vpool *pool, size_t datsize); static void vpool_shift(struct vpool *pool) { if (pool->v_buf != pool->v_basebuf) { memmove(pool->v_basebuf, pool->v_buf, pool->v_off); pool->v_buf = pool->v_basebuf; } } static int vpool_new_size(struct vpool *pool, size_t datsize, size_t *size) { size_t need; size_t rem; if (datsize <= pool->v_size - pool->v_off) { *size = pool->v_size; return (0); } /* Check limit of new requested size */ if (pool->v_limit - pool->v_off < datsize) { return (EFBIG); } need = pool->v_off + datsize; /* Check limit of new size aligned to block size */ rem = need % pool->v_blksize; if (rem != 0) { if (pool->v_limit - pool->v_off >= datsize + (pool->v_blksize - rem)) { need += pool->v_blksize - rem; } else { need = pool->v_limit; } } *size = need; return (0); } static int vpool_resize(struct vpool *pool, size_t datsize) { void *ret; size_t size; int error; error = vpool_new_size(pool, datsize, &size); if (error != 0) { return (error); } if (size > pool->v_size) { ret = malloc(size); if (ret == NULL) { return (ENOMEM); } if (pool->v_off > 0) { memcpy(ret, pool->v_buf, pool->v_off); } free(pool->v_basebuf); pool->v_basebuf = pool->v_buf = ret; pool->v_size = size; } else if ((pool->v_size - pool->v_off) - (pool->v_buf - pool->v_basebuf) < datsize) { vpool_shift(pool); } return (0); } void vpool_init(struct vpool *pool, size_t blksize, size_t limit) { pool->v_basebuf = pool->v_buf = NULL; pool->v_off = pool->v_size = 0; pool->v_blksize = (blksize == 0) ? 4096 : blksize; /* XXX */ pool->v_limit = (limit == 0) ? SIZE_MAX : limit; pool->v_lasterr = 0; } void vpool_final(struct vpool *pool) { free(pool->v_basebuf); } void vpool_reset(struct vpool *pool) { free(pool->v_basebuf); pool->v_basebuf = pool->v_buf = NULL; pool->v_off = pool->v_size = 0; pool->v_lasterr = 0; } void vpool_wipe(struct vpool *pool) { pool->v_off = 0; pool->v_lasterr = 0; } void * vpool_insert(struct vpool *pool, size_t where, void *data, size_t datsize) { void *ret; int error; error = vpool_resize(pool, datsize); if (error != 0) { pool->v_lasterr = error; return (NULL); } /* * If ``where'' is greater than or equal to offset then * we are appending data to the end of the buffer. */ if (where > pool->v_off) { where = pool->v_off; } ret = (uint8_t *)pool->v_buf + where; if (pool->v_off - where > 0) { memmove(ret + datsize, ret, pool->v_off - where); } memcpy(ret, data, datsize); pool->v_off += datsize; pool->v_lasterr = 0; return (ret); } void * vpool_expand(struct vpool *pool, size_t where, size_t size) { void *ret; int error; error = vpool_resize(pool, size); if (error != 0) { pool->v_lasterr = error; return (NULL); } /* * If ``where'' is greater than or equal to offset then * we are appending data to the end of the buffer. */ if (where > pool->v_off) { where = pool->v_off; } ret = (uint8_t *)pool->v_buf + where; if (pool->v_off - where > 0) { memmove(ret + size, ret, pool->v_off - where); } pool->v_off += size; pool->v_lasterr = 0; return (ret); } int vpool_truncate(struct vpool *pool, size_t where, size_t size, enum vpool_trunc how) { /* Check if caller wants to remove more data than we have */ if (where >= pool->v_off || size > pool->v_off || pool->v_off - size < where) { pool->v_lasterr = ERANGE; return (pool->v_lasterr); } if (how == VPOOL_EXCLUDE) { if (where == 0) { /* * Optimization. * Don't move data, just adjust pointer. */ pool->v_buf = (uint8_t *)pool->v_buf + size; } else { memmove((uint8_t *)pool->v_buf + where, (uint8_t *)pool->v_buf + where + size, pool->v_off - size - where); } pool->v_off -= size; } else { pool->v_buf = (uint8_t *)pool->v_buf + where; pool->v_off = size; } pool->v_lasterr = 0; return (0); } void vpool_export(struct vpool *pool, void **buf, size_t *size) { vpool_shift(pool); *buf = pool->v_buf; *size = pool->v_off; pool->v_basebuf = pool->v_buf = NULL; pool->v_off = pool->v_size = 0; pool->v_lasterr = 0; }