Mercurial > libjeffpc
view buffer.c @ 850:3a53836f2274
buffer: add buffer_delete
Removes arbitrary number of bytes at arbitrary offset into the buffer.
Signed-off-by: Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
author | Josef 'Jeff' Sipek <jeffpc@josefsipek.net> |
---|---|
date | Thu, 16 Sep 2021 23:34:34 -0400 |
parents | 1c4d7ff0a682 |
children |
line wrap: on
line source
/* * Copyright (c) 2017-2021 Josef 'Jeff' Sipek <jeffpc@josefsipek.net> * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include <stdbool.h> #include <jeffpc/int.h> #include "buffer_impl.h" struct buffer *buffer_alloc(size_t expected_size) { struct buffer *buffer; int ret; buffer = malloc(sizeof(struct buffer)); if (!buffer) return ERR_PTR(-ENOMEM); ret = buffer_init_heap(buffer, expected_size); if (ret) { free(buffer); return ERR_PTR(ret); } buffer->heap = true; return buffer; } void buffer_free(struct buffer *buffer) { if (!buffer) return; if (buffer->ops->free) buffer->ops->free(buffer->data); if (buffer->heap) free(buffer); } int buffer_init_heap(struct buffer *buffer, size_t expected_size) { buffer->data = malloc(expected_size ? expected_size : 1); if (!buffer->data) return -ENOMEM; buffer->off = 0; buffer->size = 0; buffer->allocsize = expected_size; buffer->ops = &heap_buffer; buffer->heap = false; return 0; } void buffer_init_sink(struct buffer *buffer) { buffer->data = NULL; buffer->off = 0; buffer->size = 0; buffer->allocsize = SIZE_MAX; buffer->ops = &sink_buffer; buffer->heap = false; } void buffer_init_static(struct buffer *buffer, const void *data, size_t size, size_t bufsize, bool writable) { ASSERT3U(size, <=, bufsize); buffer->data = (void *) data; buffer->off = 0; buffer->size = size; buffer->allocsize = bufsize; buffer->ops = writable ? &static_buffer_rw : &static_buffer_ro; buffer->heap = false; } static int resize(struct buffer *buffer, size_t newsize) { void *tmp; if (newsize <= buffer->allocsize) return 0; if (!buffer->ops->realloc) return -ENOTSUP; tmp = buffer->ops->realloc(buffer->data, newsize); if (!tmp) return -ENOMEM; buffer->data = tmp; buffer->allocsize = newsize; return 0; } int buffer_insert(struct buffer *buffer, const void *data, size_t size, size_t off) { int ret; if (!buffer) return -EINVAL; if (!data && size) return -EINVAL; if (buffer->ops->check_insert) { ret = buffer->ops->check_insert(buffer, data, size, off); if (ret) return ret; } if (off > buffer->size) return -EINVAL; /* can't insert past EOF */ if (!size) return 0; /* insert(..., 0, ...) is a no-op */ ret = resize(buffer, buffer->size + size); if (ret) return ret; if (off < buffer->size) buffer->ops->move(buffer, off + size, off, buffer->size - off); buffer->ops->copyin(buffer, off, data, size); buffer->size += size; return 0; } int buffer_copy(struct buffer *dst, struct buffer *src, size_t len) { /* * TODO: we could make this more efficient by growing the * destination buffer only once */ while (len) { uint8_t tmp[4096]; ssize_t ret; /* read in a bunch */ ret = buffer_read(src, tmp, MIN(sizeof(tmp), len)); if (ret < 0) return ret; /* decrement now, so we can reuse ret below */ len -= ret; /* and append whatever we read */ ret = buffer_append(dst, tmp, ret); if (ret) return ret; } return 0; } int buffer_delete(struct buffer *buffer, size_t offset, size_t len) { int ret; if (!buffer) return -EINVAL; if (buffer->ops->check_delete) { ret = buffer->ops->check_delete(buffer, offset, len); if (ret) return ret; } if (!len) return 0; /* delete(..., 0) is a no-op */ if (offset >= buffer->size) return -EINVAL; len = MIN(len, buffer->size - offset); if ((offset + len) < buffer->size) buffer->ops->move(buffer, offset, offset + len, buffer->size - offset - len); buffer->size -= len; return 0; } ssize_t buffer_seek(struct buffer *buffer, off_t offset, int whence) { size_t newoff; if (!buffer) return -EINVAL; switch (whence) { case SEEK_SET: if (offset < 0) return -EINVAL; if (offset > buffer->size) return -EINVAL; newoff = offset; break; case SEEK_CUR: if ((offset > 0) && (offset > (buffer->size - buffer->off))) return -EINVAL; if ((offset < 0) && (-offset > buffer->off)) return -EINVAL; newoff = buffer->off + offset; break; case SEEK_END: if (offset > 0) return -EINVAL; if (-offset > buffer->size) return -EINVAL; newoff = buffer->size + offset; break; default: return -EINVAL; } if (newoff > SSIZE_MAX) return -EOVERFLOW; if (buffer->ops->check_seek) { ssize_t ret; ret = buffer->ops->check_seek(buffer, offset, whence, newoff); if (ret) return ret; } buffer->off = newoff; return newoff; } int buffer_truncate(struct buffer *buffer, size_t size) { int ret; if (!buffer) return -EINVAL; if (buffer->ops->check_truncate) { ret = buffer->ops->check_truncate(buffer, size); if (ret) return ret; } ret = resize(buffer, size); if (ret) return ret; /* clear any expansion */ if (buffer->size < size) buffer->ops->clear(buffer, buffer->size, size - buffer->size); buffer->size = size; buffer->off = MIN(buffer->off, size); return 0; } ssize_t buffer_pread(struct buffer *buffer, void *buf, size_t len, size_t off) { ssize_t ret; if (!buffer || !buf) return -EINVAL; if (buffer->ops->check_read) { ret = buffer->ops->check_read(buffer, buf, len, off); if (ret) return ret; } if (off >= buffer->size) ret = 0; else if ((off + len) > buffer->size) ret = buffer->size - off; else ret = len; if (ret) buffer->ops->copyout(buffer, off, buf, ret); return ret; } ssize_t buffer_pwrite(struct buffer *buffer, const void *buf, size_t len, size_t off) { ssize_t ret; if (!buffer || !buf) return -EINVAL; if (buffer->ops->check_write) { ret = buffer->ops->check_write(buffer, buf, len, off); if (ret) return ret; } ret = resize(buffer, off + len); if (ret) return ret; /* clear any holes */ if (buffer->size < off) buffer->ops->clear(buffer, buffer->size, off - buffer->size); buffer->ops->copyin(buffer, off, buf, len); buffer->size = MAX(buffer->size, off + len); return len; } /* * Generic implementations */ /* clear implementations */ void generic_buffer_clear_memset(struct buffer *buffer, size_t off, size_t len) { memset(buffer->data + off, 0, len); } void generic_buffer_clear_nop(struct buffer *buffer, size_t off, size_t len) { } void generic_buffer_clear_panic(struct buffer *buffer, size_t off, size_t len) { panic("buffer clear called"); } /* copyin implementations */ void generic_buffer_copyin_memcpy(struct buffer *buffer, size_t off, const void *newdata, size_t newdatalen) { memcpy(buffer->data + off, newdata, newdatalen); } void generic_buffer_copyin_nop(struct buffer *buffer, size_t off, const void *newdata, size_t newdatalen) { } void generic_buffer_copyin_panic(struct buffer *buffer, size_t off, const void *newdata, size_t newdatalen) { panic("buffer copyin called"); } /* copyout implementations */ void generic_buffer_copyout_memcpy(struct buffer *buffer, size_t off, void *data, size_t datalen) { memcpy(data, buffer->data + off, datalen); } void generic_buffer_copyout_nop(struct buffer *buffer, size_t off, void *data, size_t datalen) { } void generic_buffer_copyout_panic(struct buffer *buffer, size_t off, void *data, size_t datalen) { panic("buffer copyout called"); } /* move implementations */ void generic_buffer_move_memmove(struct buffer *buffer, size_t dst, size_t src, size_t len) { memmove(buffer->data + dst, buffer->data + src, len); } void generic_buffer_move_nop(struct buffer *buffer, size_t dst, size_t src, size_t len) { } void generic_buffer_move_panic(struct buffer *buffer, size_t dst, size_t src, size_t len) { panic("buffer move called"); }