universe@483: /* universe@483: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. universe@483: * universe@483: * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. universe@483: * universe@483: * Redistribution and use in source and binary forms, with or without universe@483: * modification, are permitted provided that the following conditions are met: universe@483: * universe@483: * 1. Redistributions of source code must retain the above copyright universe@483: * notice, this list of conditions and the following disclaimer. universe@483: * universe@483: * 2. Redistributions in binary form must reproduce the above copyright universe@483: * notice, this list of conditions and the following disclaimer in the universe@483: * documentation and/or other materials provided with the distribution. universe@483: * universe@483: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" universe@483: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE universe@483: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE universe@483: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE universe@483: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR universe@483: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF universe@483: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS universe@483: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN universe@483: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) universe@483: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE universe@483: * POSSIBILITY OF SUCH DAMAGE. universe@483: */ universe@483: universe@483: #include "cx/buffer.h" universe@483: #include "cx/utils.h" universe@483: universe@483: #include universe@530: #include universe@483: #include universe@539: #include universe@483: universe@501: int cxBufferInit( universe@501: CxBuffer *buffer, universe@483: void *space, universe@483: size_t capacity, universe@529: CxAllocator const *allocator, universe@483: int flags universe@483: ) { universe@501: buffer->allocator = allocator; universe@501: buffer->flags = flags; universe@501: if (!space) { universe@501: buffer->bytes = cxMalloc(allocator, capacity); universe@501: if (buffer->bytes == NULL) { universe@501: return 1; universe@483: } universe@501: buffer->flags |= CX_BUFFER_FREE_CONTENTS; universe@501: } else { universe@501: buffer->bytes = space; universe@501: } universe@501: buffer->capacity = capacity; universe@501: buffer->size = 0; universe@539: buffer->pos = 0; universe@483: universe@539: buffer->flush_func = NULL; universe@541: buffer->flush_target = NULL; universe@539: buffer->flush_blkmax = 0; universe@539: buffer->flush_blksize = 4096; universe@539: buffer->flush_threshold = SIZE_MAX; universe@483: universe@501: return 0; universe@483: } universe@483: universe@500: void cxBufferDestroy(CxBuffer *buffer) { universe@483: if ((buffer->flags & CX_BUFFER_FREE_CONTENTS) == CX_BUFFER_FREE_CONTENTS) { universe@501: cxFree(buffer->allocator, buffer->bytes); universe@483: } universe@483: } universe@483: universe@483: int cxBufferSeek( universe@500: CxBuffer *buffer, universe@483: off_t offset, universe@483: int whence universe@483: ) { universe@483: size_t npos; universe@483: switch (whence) { universe@483: case SEEK_CUR: universe@483: npos = buffer->pos; universe@483: break; universe@483: case SEEK_END: universe@483: npos = buffer->size; universe@483: break; universe@483: case SEEK_SET: universe@483: npos = 0; universe@483: break; universe@483: default: universe@483: return -1; universe@483: } universe@483: universe@483: size_t opos = npos; universe@483: npos += offset; universe@483: universe@483: if ((offset > 0 && npos < opos) || (offset < 0 && npos > opos)) { universe@483: return -1; universe@483: } universe@483: universe@483: if (npos >= buffer->size) { universe@483: return -1; universe@483: } else { universe@483: buffer->pos = npos; universe@483: return 0; universe@483: } universe@483: universe@483: } universe@483: universe@529: void cxBufferClear(CxBuffer *buffer) { universe@529: memset(buffer->bytes, 0, buffer->size); universe@529: buffer->size = 0; universe@529: buffer->pos = 0; universe@529: } universe@529: universe@529: int cxBufferEof(CxBuffer const *buffer) { universe@483: return buffer->pos >= buffer->size; universe@483: } universe@483: universe@483: int cxBufferMinimumCapacity( universe@500: CxBuffer *buffer, universe@532: size_t newcap universe@483: ) { universe@532: if (newcap <= buffer->capacity) { universe@532: return 0; universe@483: } universe@483: universe@536: if (cxReallocate(buffer->allocator, universe@536: (void **) &buffer->bytes, newcap) == 0) { universe@483: buffer->capacity = newcap; universe@533: return 0; universe@483: } else { universe@483: return -1; universe@483: } universe@483: } universe@483: universe@483: size_t cxBufferWrite( universe@489: void const *ptr, universe@483: size_t size, universe@483: size_t nitems, universe@500: CxBuffer *buffer universe@483: ) { universe@539: // TODO: optimize for special case size == nitems == 1 universe@483: size_t len; universe@483: if (cx_szmul(size, nitems, &len)) { universe@483: return 0; universe@483: } universe@483: size_t required = buffer->pos + len; universe@483: if (buffer->pos > required) { universe@483: return 0; universe@483: } universe@483: universe@539: bool perform_flush = false; universe@483: if (required > buffer->capacity) { universe@539: if ((buffer->flags & CX_BUFFER_AUTO_EXTEND) == CX_BUFFER_AUTO_EXTEND && required) { universe@539: if (buffer->flush_blkmax > 0 && required > buffer->flush_threshold) { universe@539: perform_flush = true; universe@539: } else { universe@539: if (cxBufferMinimumCapacity(buffer, required)) { universe@539: return 0; universe@539: } universe@483: } universe@483: } else { universe@539: if (buffer->flush_blkmax > 0) { universe@539: perform_flush = true; universe@539: } else { universe@539: // truncate data to be written, if we can neither extend nor flush universe@539: len = buffer->capacity - buffer->pos; universe@539: if (size > 1) { universe@539: // TODO: this is bugged - it would only discard one element and not as many as required universe@539: len -= len % size; universe@539: } universe@540: nitems = len / size; universe@483: } universe@483: } universe@483: } universe@483: universe@483: if (len == 0) { universe@483: return len; universe@483: } universe@483: universe@539: if (perform_flush) { universe@539: // TODO: implement flushing universe@539: // (1) determine how many bytes to flush (use flushmax = blkmax * blksize) universe@539: // (2) if len is larger than the number computed in (1) we need more flush cycles, compute how many universe@539: // (3) determine how many bytes from the buffer shall be flushed universe@539: // (4) if something remains in the buffer, shift the buffer to the left universe@539: // (4a) if buffer was shifted, append the new data to the buffer universe@539: // (4b) if the buffer was flushed entirely AND the new data also fits into flushmax, universe@539: // directly write the new data to the flush sink universe@539: return 0; // remove this after implementation universe@539: } else { universe@539: memcpy(buffer->bytes + buffer->pos, ptr, len); universe@539: buffer->pos += len; universe@539: if (buffer->pos > buffer->size) { universe@539: buffer->size = buffer->pos; universe@539: } universe@483: } universe@483: universe@539: return nitems; universe@483: } universe@483: universe@538: int cxBufferPut( universe@538: CxBuffer *buffer, universe@538: int c universe@538: ) { universe@538: c &= 0xFF; universe@538: unsigned char const ch = c; universe@538: if (cxBufferWrite(&ch, 1, 1, buffer) == 1) { universe@538: return c; universe@538: } else { universe@538: return EOF; universe@538: } universe@538: } universe@538: universe@538: size_t cxBufferPutString( universe@538: CxBuffer *buffer, universe@538: const char *str universe@538: ) { universe@538: return cxBufferWrite(str, 1, strlen(str), buffer); universe@538: } universe@538: universe@483: size_t cxBufferRead( universe@483: void *ptr, universe@483: size_t size, universe@483: size_t nitems, universe@500: CxBuffer *buffer universe@483: ) { universe@483: size_t len; universe@483: if (cx_szmul(size, nitems, &len)) { universe@483: return 0; universe@483: } universe@483: if (buffer->pos + len > buffer->size) { universe@483: len = buffer->size - buffer->pos; universe@483: if (size > 1) len -= len % size; universe@483: } universe@483: universe@483: if (len <= 0) { universe@483: return len; universe@483: } universe@483: universe@483: memcpy(ptr, buffer->bytes + buffer->pos, len); universe@483: buffer->pos += len; universe@483: universe@483: return len / size; universe@483: } universe@483: universe@500: int cxBufferGet(CxBuffer *buffer) { universe@483: if (cxBufferEof(buffer)) { universe@483: return EOF; universe@483: } else { universe@483: int c = buffer->bytes[buffer->pos]; universe@483: buffer->pos++; universe@483: return c; universe@483: } universe@483: } universe@483: universe@483: int cxBufferShiftLeft( universe@500: CxBuffer *buffer, universe@483: size_t shift universe@483: ) { universe@483: if (shift >= buffer->size) { universe@483: buffer->pos = buffer->size = 0; universe@483: } else { universe@483: memmove(buffer->bytes, buffer->bytes + shift, buffer->size - shift); universe@483: buffer->size -= shift; universe@483: universe@483: if (buffer->pos >= shift) { universe@483: buffer->pos -= shift; universe@483: } else { universe@483: buffer->pos = 0; universe@483: } universe@483: } universe@483: return 0; universe@483: } universe@483: universe@483: int cxBufferShiftRight( universe@500: CxBuffer *buffer, universe@483: size_t shift universe@483: ) { universe@483: size_t req_capacity = buffer->size + shift; universe@483: size_t movebytes; universe@483: universe@483: // auto extend buffer, if required and enabled universe@483: if (buffer->capacity < req_capacity) { universe@483: if ((buffer->flags & CX_BUFFER_AUTO_EXTEND) == CX_BUFFER_AUTO_EXTEND) { universe@483: if (cxBufferMinimumCapacity(buffer, req_capacity)) { universe@483: return 1; universe@483: } universe@483: movebytes = buffer->size; universe@483: } else { universe@483: movebytes = buffer->capacity - shift; universe@483: } universe@483: } else { universe@483: movebytes = buffer->size; universe@483: } universe@483: universe@483: memmove(buffer->bytes + shift, buffer->bytes, movebytes); universe@483: buffer->size = shift + movebytes; universe@483: universe@483: buffer->pos += shift; universe@483: if (buffer->pos > buffer->size) { universe@483: buffer->pos = buffer->size; universe@483: } universe@483: universe@483: return 0; universe@483: } universe@483: universe@483: int cxBufferShift( universe@500: CxBuffer *buffer, universe@483: off_t shift universe@483: ) { universe@483: if (shift < 0) { universe@483: return cxBufferShiftLeft(buffer, (size_t) (-shift)); universe@483: } else if (shift > 0) { universe@483: return cxBufferShiftRight(buffer, (size_t) shift); universe@483: } else { universe@483: return 0; universe@483: } universe@483: }