Tue, 04 Oct 2022 19:25:07 +0200
fix over-optimization of strstr
1. it's actually less performant to frequently read bytes
from an array instead of using the native word length
2. the SBO buffer should be local and not static to allow
multi-threading usage
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * \file buffer.h * * \brief Advanced buffer implementation. * * Instances of CxBuffer can be used to read from or to write to like one * would do with a stream. * * Some features for convenient use of the buffer * can be enabled. See the documentation of the macro constants for more * information. * * \author Mike Becker * \author Olaf Wintermann * \version 3.0 * \copyright 2-Clause BSD License */ #ifndef UCX_BUFFER_H #define UCX_BUFFER_H #include "common.h" #include "allocator.h" #ifdef __cplusplus extern "C" { #endif /** * No buffer features enabled (all flags cleared). */ #define CX_BUFFER_DEFAULT 0x00 /** * If this flag is enabled, the buffer will automatically free its contents when destroyed. */ #define CX_BUFFER_FREE_CONTENTS 0x01 /** * If this flag is enabled, the buffer will automatically extends its capacity. */ #define CX_BUFFER_AUTO_EXTEND 0x02 /** Structure for the UCX buffer data. */ typedef struct { /** A pointer to the buffer contents. */ union { /** * Data is interpreted as text. */ char *space; /** * Data is interpreted as binary. */ unsigned char *bytes; }; /** The allocator to use for automatic memory management. */ CxAllocator const *allocator; /** Current position of the buffer. */ size_t pos; /** Current capacity (i.e. maximum size) of the buffer. */ size_t capacity; /** Current size of the buffer content. */ size_t size; /** * The buffer may not extend beyond this threshold before starting to flush. * Default is \c SIZE_MAX (flushing disabled when auto extension is enabled). */ size_t flush_threshold; /** * The block size for the elements to flush. * Default is 4096 bytes. */ size_t flush_blksize; /** * The maximum number of blocks to flush in one cycle. * Zero disables flushing entirely (this is the default). * Set this to \c SIZE_MAX to flush the entire buffer. * * @attention if the maximum number of blocks multiplied with the block size * is smaller than the expected contents written to this buffer within one write * operation, multiple flush cycles are performed after that write. * That means the total number of blocks flushed after one write to this buffer may * be larger than \c flush_blkmax. */ size_t flush_blkmax; /** * The write function used for flushing. * If NULL, the flushed content gets discarded. */ cx_write_func flush_func; /** * The target for \c flush_func. */ void *flush_target; /** * Flag register for buffer features. * @see #CX_BUFFER_DEFAULT * @see #CX_BUFFER_FREE_CONTENTS * @see #CX_BUFFER_AUTO_EXTEND */ int flags; } cx_buffer_s; /** * UCX buffer. */ typedef cx_buffer_s CxBuffer; /** * Initializes a fresh buffer. * * \note You may provide \c NULL as argument for \p space. * Then this function will allocate the space and enforce * the #CX_BUFFER_FREE_CONTENTS flag. * * @param buffer the buffer to initialize * @param space pointer to the memory area, or \c NULL to allocate * new memory * @param capacity the capacity of the buffer * @param allocator the allocator this buffer shall use for automatic memory management * @param flags buffer features (see cx_buffer_s.flags) * @return zero on success, non-zero if a required allocation failed */ __attribute__((__nonnull__(1, 4))) int cxBufferInit( CxBuffer *buffer, void *space, size_t capacity, CxAllocator const *allocator, int flags ); /** * Destroys the buffer contents. * * Has no effect if the #CX_BUFFER_FREE_CONTENTS feature is not enabled. * * @param buffer the buffer which contents shall be destroyed */ __attribute__((__nonnull__)) void cxBufferDestroy(CxBuffer *buffer); /** * Shifts the contents of the buffer by the given offset. * * If the offset is positive, the contents are shifted to the right. * If auto extension is enabled, the buffer grows, if necessary. * In case the auto extension fails, this function returns a non-zero value and * no contents are changed. * If auto extension is disabled, the contents that do not fit into the buffer * are discarded. * * If the offset is negative, the contents are shifted to the left where the * first \p shift bytes are discarded. * The new size of the buffer is the old size minus the absolute shift value. * If this value is larger than the buffer size, the buffer is emptied (but * not cleared, see the security note below). * * The buffer position gets shifted alongside with the content but is kept * within the boundaries of the buffer. * * \note For situations where \c off_t is not large enough, there are specialized cxBufferShiftLeft() and * cxBufferShiftRight() functions using a \c size_t as parameter type. * * \attention * Security Note: The shifting operation does \em not erase the previously occupied memory cells. * But you can easily do that manually, e.g. by calling * <code>memset(buffer->bytes, 0, shift)</code> for a right shift or * <code>memset(buffer->bytes + buffer->size, 0, buffer->capacity - buffer->size)</code> * for a left shift. * * @param buffer the buffer * @param shift the shift offset (negative means left shift) * @return 0 on success, non-zero if a required auto-extension fails */ __attribute__((__nonnull__)) int cxBufferShift( CxBuffer *buffer, off_t shift ); /** * Shifts the buffer to the right. * See cxBufferShift() for details. * * @param buffer the buffer * @param shift the shift offset * @return 0 on success, non-zero if a required auto-extension fails * @see cxBufferShift() */ __attribute__((__nonnull__)) int cxBufferShiftRight( CxBuffer *buffer, size_t shift ); /** * Shifts the buffer to the left. * See cxBufferShift() for details. * * \note Since a left shift cannot fail due to memory allocation problems, this * function always returns zero. * * @param buffer the buffer * @param shift the positive shift offset * @return always zero * @see cxBufferShift() */ __attribute__((__nonnull__)) int cxBufferShiftLeft( CxBuffer *buffer, size_t shift ); /** * Moves the position of the buffer. * * The new position is relative to the \p whence argument. * * \li \c SEEK_SET marks the start of the buffer. * \li \c SEEK_CUR marks the current position. * \li \c SEEK_END marks the end of the buffer. * * With an offset of zero, this function sets the buffer position to zero * (\c SEEK_SET), the buffer size (\c SEEK_END) or leaves the buffer position * unchanged (\c SEEK_CUR). * * @param buffer the buffer * @param offset position offset relative to \p whence * @param whence one of \c SEEK_SET, \c SEEK_CUR or \c SEEK_END * @return 0 on success, non-zero if the position is invalid * */ __attribute__((__nonnull__)) int cxBufferSeek( CxBuffer *buffer, off_t offset, int whence ); /** * Clears the buffer by resetting the position and deleting the data. * * The data is deleted by zeroing it with a call to memset(). * * @param buffer the buffer to be cleared */ __attribute__((__nonnull__)) void cxBufferClear(CxBuffer *buffer); /** * Tests, if the buffer position has exceeded the buffer capacity. * * @param buffer the buffer to test * @return non-zero, if the current buffer position has exceeded the last * available byte of the buffer. */ __attribute__((__nonnull__)) int cxBufferEof(CxBuffer const *buffer); /** * Ensures that the buffer has a minimum capacity. * * If the current capacity is not sufficient, the buffer will be extended. * * @param buffer the buffer * @param capacity the minimum required capacity for this buffer * @return 0 on success or a non-zero value on failure */ __attribute__((__nonnull__)) int cxBufferMinimumCapacity( CxBuffer *buffer, size_t capacity ); /** * Writes data to a CxBuffer. * * If flushing is enabled and the buffer needs to flush, the data is flushed to * the target until the target signals that it cannot take more data by * returning zero via the respective write function. In that case, the remaining * data in this buffer is shifted to the beginning of this buffer so that the * newly available space can be used to append as much data as possible. This * function only stops writing more elements, when the flush target and this * buffer are both incapable of taking more data or all data has been written. * The number returned by this function is the total number of elements that * could be written during the process. It does not necessarily mean that those * elements are still in this buffer, because some of them could have also be * flushed already. * * If automatic flushing is not enabled, the position of the buffer is increased * by the number of bytes written. * * \note The signature is compatible with the fwrite() family of functions. * * @param ptr a pointer to the memory area containing the bytes to be written * @param size the length of one element * @param nitems the element count * @param buffer the CxBuffer to write to * @return the total count of elements written */ __attribute__((__nonnull__)) size_t cxBufferWrite( void const *ptr, size_t size, size_t nitems, CxBuffer *buffer ); /** * Reads data from a CxBuffer. * * The position of the buffer is increased by the number of bytes read. * * \note The signature is compatible with the fread() family of functions. * * @param ptr a pointer to the memory area where to store the read data * @param size the length of one element * @param nitems the element count * @param buffer the CxBuffer to read from * @return the total number of elements read */ __attribute__((__nonnull__)) size_t cxBufferRead( void *ptr, size_t size, size_t nitems, CxBuffer *buffer ); /** * Writes a character to a buffer. * * The least significant byte of the argument is written to the buffer. If the * end of the buffer is reached and #CX_BUFFER_AUTO_EXTEND feature is enabled, * the buffer capacity is extended by cxBufferMinimumCapacity(). If the feature is * disabled or buffer extension fails, \c EOF is returned. * * On successful write, the position of the buffer is increased. * * @param buffer the buffer to write to * @param c the character to write * @return the byte that has bean written or \c EOF when the end of the stream is * reached and automatic extension is not enabled or not possible */ __attribute__((__nonnull__)) int cxBufferPut( CxBuffer *buffer, int c ); /** * Writes a string to a buffer. * * @param buffer the buffer * @param str the zero-terminated string * @return the number of bytes written */ __attribute__((__nonnull__)) size_t cxBufferPutString( CxBuffer *buffer, const char *str ); /** * Gets a character from a buffer. * * The current position of the buffer is increased after a successful read. * * @param buffer the buffer to read from * @return the character or \c EOF, if the end of the buffer is reached */ __attribute__((__nonnull__)) int cxBufferGet(CxBuffer *buffer); #ifdef __cplusplus } #endif #endif /* UCX_BUFFER_H */