Mon, 27 Dec 2021 16:51:10 +0100
add ported UCX buffer implementation
Notes:
* ucx_buffer_extend has been removed
in favor of cxBufferMinimumCapacity
* the buffer struct now has a union for
char* and unsigned char* buffers
src/CMakeLists.txt | file | annotate | diff | comparison | revisions | |
src/buffer.c | file | annotate | diff | comparison | revisions | |
src/cx/buffer.h | file | annotate | diff | comparison | revisions | |
src/cx/common.h | file | annotate | diff | comparison | revisions | |
src/cx/utils.h | file | annotate | diff | comparison | revisions | |
src/utils.c | file | annotate | diff | comparison | revisions |
--- a/src/CMakeLists.txt Mon Dec 27 14:44:08 2021 +0100 +++ b/src/CMakeLists.txt Mon Dec 27 16:51:10 2021 +0100 @@ -1,13 +1,17 @@ set(sources + utils.c allocator.c linked_list.c tree.c + buffer.c ) set(headers + cx/utils.h cx/allocator.h cx/list.h cx/linked_list.h cx/tree.h + cx/buffer.h ) add_library(ucx SHARED ${sources})
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/buffer.c Mon Dec 27 16:51:10 2021 +0100 @@ -0,0 +1,328 @@ +/* + * 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. + */ + +#include "cx/buffer.h" +#include "cx/utils.h" + +#include <stdlib.h> +#include <string.h> + +CxBuffer cxBufferCreate( + void *space, + size_t capacity, + int flags +) { + CxBuffer buffer = (CxBuffer) malloc(sizeof(cx_buffer_s)); + if (buffer) { + buffer->flags = flags; + if (!space) { + buffer->bytes = malloc(capacity); + if (!buffer->bytes) { + free(buffer); + return NULL; + } + memset(buffer->bytes, 0, capacity); + buffer->flags |= CX_BUFFER_FREE_CONTENTS; + } else { + buffer->bytes = space; + } + buffer->capacity = capacity; + buffer->size = 0; + + buffer->pos = 0; + } + + return buffer; +} + +void cxBufferDestroy(CxBuffer buffer) { + if ((buffer->flags & CX_BUFFER_FREE_CONTENTS) == CX_BUFFER_FREE_CONTENTS) { + free(buffer->bytes); + } + free(buffer); +} + +CxBuffer cxBufferExtract( + CxBuffer src, + size_t start, + size_t length, + int flags +) { + if (src->size == 0 || length == 0 || + ((size_t) -1) - start < length || start + length > src->capacity) { + return NULL; + } + + CxBuffer dst = (CxBuffer) malloc(sizeof(cx_buffer_s)); + if (dst) { + dst->bytes = malloc(length); + if (!dst->bytes) { + free(dst); + return NULL; + } + dst->capacity = length; + dst->size = length; + dst->flags = flags | CX_BUFFER_FREE_CONTENTS; + dst->pos = 0; + memcpy(dst->bytes, src->bytes + start, length); + } + return dst; +} + +int cxBufferSeek( + CxBuffer buffer, + off_t offset, + int whence +) { + size_t npos; + switch (whence) { + case SEEK_CUR: + npos = buffer->pos; + break; + case SEEK_END: + npos = buffer->size; + break; + case SEEK_SET: + npos = 0; + break; + default: + return -1; + } + + size_t opos = npos; + npos += offset; + + if ((offset > 0 && npos < opos) || (offset < 0 && npos > opos)) { + return -1; + } + + if (npos >= buffer->size) { + return -1; + } else { + buffer->pos = npos; + return 0; + } + +} + +int cxBufferEof(CxBuffer buffer) { + return buffer->pos >= buffer->size; +} + +int cxBufferMinimumCapacity( + CxBuffer buffer, + size_t additional_bytes +) { + size_t newcap = buffer->capacity + additional_bytes; + + // overflow protection + if (newcap < buffer->capacity) { + return -1; + } + + unsigned char *newspace = realloc(buffer->bytes, newcap); + if (newspace) { + memset(newspace + buffer->size, 0, newcap - buffer->size); + buffer->bytes = newspace; + buffer->capacity = newcap; + } else { + return -1; + } + + return 0; +} + +size_t cxBufferWrite( + const void *ptr, + size_t size, + size_t nitems, + CxBuffer buffer +) { + size_t len; + if (cx_szmul(size, nitems, &len)) { + return 0; + } + size_t required = buffer->pos + len; + if (buffer->pos > required) { + return 0; + } + + if (required > buffer->capacity) { + if ((buffer->flags & CX_BUFFER_AUTO_EXTEND) == CX_BUFFER_AUTO_EXTEND) { + if (cxBufferMinimumCapacity(buffer, required)) { + return 0; + } + } else { + len = buffer->capacity - buffer->pos; + if (size > 1) { + len -= len % size; + } + } + } + + if (len == 0) { + return len; + } + + memcpy(buffer->bytes + buffer->pos, ptr, len); + buffer->pos += len; + if (buffer->pos > buffer->size) { + buffer->size = buffer->pos; + } + + return len / size; +} + +size_t cxBufferRead( + void *ptr, + size_t size, + size_t nitems, + CxBuffer buffer +) { + size_t len; + if (cx_szmul(size, nitems, &len)) { + return 0; + } + if (buffer->pos + len > buffer->size) { + len = buffer->size - buffer->pos; + if (size > 1) len -= len % size; + } + + if (len <= 0) { + return len; + } + + memcpy(ptr, buffer->bytes + buffer->pos, len); + buffer->pos += len; + + return len / size; +} + +int cxBufferPut( + CxBuffer buffer, + int c +) { + if (buffer->pos >= buffer->capacity) { + if ((buffer->flags & CX_BUFFER_AUTO_EXTEND) == CX_BUFFER_AUTO_EXTEND) { + if (cxBufferMinimumCapacity(buffer, buffer->capacity + 1)) { + return EOF; + } + } else { + return EOF; + } + } + + c &= 0xFF; + buffer->bytes[buffer->pos] = (unsigned char) c; + buffer->pos++; + if (buffer->pos > buffer->size) { + buffer->size = buffer->pos; + } + return c; +} + +int cxBufferGet(CxBuffer buffer) { + if (cxBufferEof(buffer)) { + return EOF; + } else { + int c = buffer->bytes[buffer->pos]; + buffer->pos++; + return c; + } +} + +size_t cxBufferPutString( + CxBuffer buffer, + const char *str +) { + return cxBufferWrite(str, 1, strlen(str), buffer); +} + +int cxBufferShiftLeft( + CxBuffer buffer, + size_t shift +) { + if (shift >= buffer->size) { + buffer->pos = buffer->size = 0; + } else { + memmove(buffer->bytes, buffer->bytes + shift, buffer->size - shift); + buffer->size -= shift; + + if (buffer->pos >= shift) { + buffer->pos -= shift; + } else { + buffer->pos = 0; + } + } + return 0; +} + +int cxBufferShiftRight( + CxBuffer buffer, + size_t shift +) { + size_t req_capacity = buffer->size + shift; + size_t movebytes; + + // auto extend buffer, if required and enabled + if (buffer->capacity < req_capacity) { + if ((buffer->flags & CX_BUFFER_AUTO_EXTEND) == CX_BUFFER_AUTO_EXTEND) { + if (cxBufferMinimumCapacity(buffer, req_capacity)) { + return 1; + } + movebytes = buffer->size; + } else { + movebytes = buffer->capacity - shift; + } + } else { + movebytes = buffer->size; + } + + memmove(buffer->bytes + shift, buffer->bytes, movebytes); + buffer->size = shift + movebytes; + + buffer->pos += shift; + if (buffer->pos > buffer->size) { + buffer->pos = buffer->size; + } + + return 0; +} + +int cxBufferShift( + CxBuffer buffer, + off_t shift +) { + if (shift < 0) { + return cxBufferShiftLeft(buffer, (size_t) (-shift)); + } else if (shift > 0) { + return cxBufferShiftRight(buffer, (size_t) shift); + } else { + return 0; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cx/buffer.h Mon Dec 27 16:51:10 2021 +0100 @@ -0,0 +1,377 @@ +/* + * 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 <sys/types.h> +#include <stdio.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; + }; + /** 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; + /** + * 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; + +/** + * Creates a new 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 space pointer to the memory area, or <code>NULL</code> to allocate + * new memory + * \param capacity the capacity of the buffer + * \param flags buffer features (see cx_buffer_s.flags) + * \return the new buffer + */ +CxBuffer cxBufferCreate( + void *space, + size_t capacity, + int flags +); + +/** + * Destroys a buffer. + * + * If the #CX_BUFFER_FREE_CONTENTS feature is enabled, the contents of the buffer + * are also freed. + * + * \param buffer the buffer to destroy + */ +void cxBufferDestroy(CxBuffer buffer); + +/** + * Creates a new buffer and fills it with content copied from another buffer. + * + * \note The #CX_BUFFER_FREE_CONTENTS feature is enforced for the new buffer. + * + * \param src the source buffer + * \param start the start position of extraction + * \param length the count of bytes to extract (must not be zero) + * \param flags features for the new buffer (#CX_BUFFER_FREE_CONTENTS will always be enabled) + * \return a new buffer containing the extraction + */ +CxBuffer cxBufferExtract( + CxBuffer src, + size_t start, + size_t length, + int flags +); + +/** + * A shorthand macro for copying an entire buffer. + * + * \param src the source buffer + * \param flags features for the new buffer (#CX_BUFFER_FREE_CONTENTS will always be enabled) + * \return a new buffer with the copied content + */ +#define cxBufferClone(src, flags) cxBufferExtract(src, 0, (src)->capacity, flags) + + +/** + * 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. + * + * \par Security Note + * The shifting operation does \em not erase the previously occupied memory cells. + * 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->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 + */ +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() + */ +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() + */ +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 + * + */ +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 + */ +#define cxBufferClear(buffer) memset((buffer)->bytes, 0, (buffer)->size); \ + (buffer)->size = 0; (buffer)->pos = 0; + +/** + * 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. + */ +int cxBufferEof(CxBuffer 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 + */ +int cxBufferMinimumCapacity( + CxBuffer buffer, + size_t capacity +); + +/** + * Writes data to a CxBuffer. + * + * 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 bytes written + */ +size_t cxBufferWrite( + const void *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 + */ +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 + */ +int cxBufferPut( + CxBuffer buffer, + int c +); + +/** + * 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 + */ +int cxBufferGet(CxBuffer buffer); + +/** + * Writes a string to a buffer. + * + * \param buffer the buffer + * \param str the zero-terminated string + * \return the number of bytes written + */ +size_t cxBufferPutString( + CxBuffer buffer, + const char *str +); + +#ifdef __cplusplus +} +#endif + +#endif /* UCX_BUFFER_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cx/common.h Mon Dec 27 16:51:10 2021 +0100 @@ -0,0 +1,73 @@ +/* + * 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 common.h + * + * \brief Common definitions and feature checks. + * + * \author Mike Becker + * \author Olaf Wintermann + * \version 3.0 + * \copyright 2-Clause BSD License + */ + +#ifndef UCX_COMMON_H +#define UCX_COMMON_H + +/** Major UCX version as integer constant. */ +#define UCX_VERSION_MAJOR 3 + +/** Minor UCX version as integer constant. */ +#define UCX_VERSION_MINOR 0 + +/** Version constant which ensures to increase monotonically. */ +#define UCX_VERSION (((UCX_VERSION_MAJOR)<<16)|UCX_VERSION_MINOR) + + +#ifdef _WIN32 +#if !(defined __ssize_t_defined || defined _SSIZE_T_) +#include <BaseTsd.h> +typedef SSIZE_T ssize_t; +#define __ssize_t_defined +#define _SSIZE_T_ +#endif /* __ssize_t_defined and _SSIZE_T */ +#ifndef __WORDSIZE +#ifdef _WIN64 +#define __WORDSIZE 64 +#else +#define __WORDSIZE 32 +#endif +#endif /* __WORDSIZE */ +#else /* !_WIN32 */ + +#include <sys/types.h> + +#endif /* _WIN32 */ + +#endif /* UCX_COMMON_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cx/utils.h Mon Dec 27 16:51:10 2021 +0100 @@ -0,0 +1,120 @@ +/* + * 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 utils.h + * + * \brief General purpose utility functions. + * + * \author Mike Becker + * \author Olaf Wintermann + * \version 3.0 + * \copyright 2-Clause BSD License + */ + +#ifndef UCX_UTILS_H +#define UCX_UTILS_H + +#include "common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* ---------------------- + * cx_szmul() definition. + * ---------------------- */ + +#if (__GNUC__ >= 5 || defined(__clang__)) && !defined(CX_NO_SZMUL_BUILTIN) +#define CX_SZMUL_BUILTIN + +#if __WORDSIZE == 32 +/** + * Alias for \c __builtin_umul_overflow. + * + * Performs a multiplication of size_t values and checks for overflow. + * + * @param a first operand + * @param b second operand + * @param result a pointer to a size_t, where the result should + * be stored + * @return zero, if no overflow occurred and the result is correct, non-zero + * otherwise + */ +#define cx_szmul(a, b, result) __builtin_umul_overflow(a, b, result) +#else /* __WORDSIZE != 32 */ +/** + * Alias for \c __builtin_umull_overflow. + * + * Performs a multiplication of size_t values and checks for overflow. + * + * @param a first operand + * @param b second operand + * @param result a pointer to a size_t, where the result should + * be stored + * @return zero, if no overflow occurred and the result is correct, non-zero + * otherwise + */ +#define cx_szmul(a, b, result) __builtin_umull_overflow(a, b, result) +#endif /* __WORDSIZE */ + +#else /* no GNUC or clang bultin */ + +/** + * Performs a multiplication of size_t values and checks for overflow. + * + * @param a first operand + * @param b second operand + * @param result a pointer to a size_t, where the result should + * be stored + * @return zero, if no overflow occurred and the result is correct, non-zero + * otherwise + */ +#define cx_szmul(a, b, result) cx_szmul_impl(a, b, result) + +/** + * Performs a multiplication of size_t values and checks for overflow. + * + * This is a custom implementation in case there is no compiler builtin + * available. + * + * @param a first operand + * @param b second operand + * @param result a pointer to a size_t where the result should be stored + * @return zero, if no overflow occurred and the result is correct, non-zero + * otherwise + */ +int cx_szmul_impl(size_t a, size_t b, size_t *result); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* UCX_UTILS_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/utils.c Mon Dec 27 16:51:10 2021 +0100 @@ -0,0 +1,46 @@ +/* + * 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. + */ + +#include "cx/utils.h" + +#ifndef CX_SZMUL_BUILTIN +int cx_szmul_impl(size_t a, size_t b, size_t *result) { + if(a == 0 || b == 0) { + *result = 0; + return 0; + } + size_t r = a * b; + if(r / b == a) { + *result = r; + return 0; + } else { + *result = 0; + return 1; + } +} +#endif