Mercurial > libjeffpc
view buffer.c @ 806:12a3139a2baf
sock: use atomic_cas_ptr correctly
This bug was caused by incorrect documentation (already fixed). It resulted
in us freeing the name on successful CAS and caching the freed pointer.
Signed-off-by: Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
author | Josef 'Jeff' Sipek <jeffpc@josefsipek.net> |
---|---|
date | Fri, 10 Apr 2020 12:27:10 -0400 |
parents | 6371fb111e27 |
children | 013adc5c54dd |
line wrap: on
line source
/* * Copyright (c) 2017-2020 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; } void buffer_init_stdio(struct buffer *buffer, FILE *f) { buffer->data = NULL; buffer->off = 0; buffer->size = 0; buffer->allocsize = SIZE_MAX; buffer->ops = &stdio_buffer; buffer->private = f; 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_append(struct buffer *buffer, const void *data, size_t size) { int ret; if (!buffer) return -EINVAL; if (!data && size) return -EINVAL; if (buffer->ops->check_append) { ret = buffer->ops->check_append(buffer, data, size); if (ret) return ret; } if (!size) return 0; /* append(..., 0) is a no-op */ ret = resize(buffer, buffer->size + size); if (ret) return ret; buffer->ops->copyin(buffer, buffer->size, data, size); buffer->size += size; 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"); }