Mon, 03 Apr 2023 19:20:30 +0200
add stream copy utils - fixes #254
src/cx/common.h | file | annotate | diff | comparison | revisions | |
src/cx/utils.h | file | annotate | diff | comparison | revisions | |
src/szmul.c | file | annotate | diff | comparison | revisions | |
src/utils.c | file | annotate | diff | comparison | revisions | |
tests/test_utils.cpp | file | annotate | diff | comparison | revisions |
--- a/src/cx/common.h Mon Apr 03 19:09:31 2023 +0200 +++ b/src/cx/common.h Mon Apr 03 19:20:30 2023 +0200 @@ -104,6 +104,16 @@ void * ); +/** + * Function pointer compatible with fread-like functions. + */ +typedef size_t (*cx_read_func)( + void *, + size_t, + size_t, + void * +); + #ifdef _WIN32 #ifdef __MINGW32__
--- a/src/cx/utils.h Mon Apr 03 19:09:31 2023 +0200 +++ b/src/cx/utils.h Mon Apr 03 19:20:30 2023 +0200 @@ -107,7 +107,87 @@ */ int cx_szmul_impl(size_t a, size_t b, size_t *result); -#endif +#endif // cx_szmul + + +/** + * Reads data from a stream and writes it to another stream. + * + * @param src the source stream + * @param dest the destination stream + * @param rfnc the read function + * @param wfnc the write function + * @param buf a pointer to the copy buffer or \c NULL if a buffer + * shall be implicitly created on the heap + * @param bufsize the size of the copy buffer - if \p buf is \c NULL you can + * set this to zero to let the implementation decide + * @param n the maximum number of bytes that shall be copied. + * If this is larger than \p bufsize, the content is copied over multiple + * iterations. + * @return the total number of bytes copied + */ +__attribute__((__nonnull__(1, 2, 3, 4))) +size_t cx_stream_bncopy( + void *src, + void *dest, + cx_read_func rfnc, + cx_write_func wfnc, + char *buf, + size_t bufsize, + size_t n +); + +/** + * Reads data from a stream and writes it to another stream. + * + * @param src the source stream + * @param dest the destination stream + * @param rfnc the read function + * @param wfnc the write function + * @param buf a pointer to the copy buffer or \c NULL if a buffer + * shall be implicitly created on the heap + * @param bufsize the size of the copy buffer - if \p buf is \c NULL you can + * set this to zero to let the implementation decide + * @return total number of bytes copied + */ +#define cx_stream_bcopy(src, dest, rfnc, wfnc, buf, bufsize) \ + cx_stream_bncopy(src, dest, rfnc, wfnc, buf, bufsize, SIZE_MAX) + +/** + * Reads data from a stream and writes it to another stream. + * + * The data is temporarily stored in a stack allocated buffer. + * + * @param src the source stream + * @param dest the destination stream + * @param rfnc the read function + * @param wfnc the write function + * @param n the maximum number of bytes that shall be copied. + * @return total number of bytes copied + */ +__attribute__((__nonnull__)) +size_t cx_stream_ncopy( + void *src, + void *dest, + cx_read_func rfnc, + cx_write_func wfnc, + size_t n +); + +/** + * Reads data from a stream and writes it to another stream. + * + * The data is temporarily stored in a stack allocated buffer. + * + * @param src the source stream + * @param dest the destination stream + * @param rfnc the read function + * @param wfnc the write function + * @param n the maximum number of bytes that shall be copied. + * @return total number of bytes copied + */ +#define cx_stream_copy(src, dest, rfnc, wfnc) \ + cx_stream_ncopy(src, dest, rfnc, wfnc, SIZE_MAX) #ifdef __cplusplus }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/szmul.c Mon Apr 03 19:20:30 2023 +0200 @@ -0,0 +1,46 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2023 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. + */ + +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; + } +} \ No newline at end of file
--- a/src/utils.c Mon Apr 03 19:09:31 2023 +0200 +++ b/src/utils.c Mon Apr 03 19:20:30 2023 +0200 @@ -28,19 +28,85 @@ #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; +#define CX_STREAM_BCOPY_BUF_SIZE 8192 +#define CX_STREAM_COPY_BUF_SIZE 1024 + +size_t cx_stream_bncopy( + void *src, + void *dest, + cx_read_func rfnc, + cx_write_func wfnc, + char *buf, + size_t bufsize, + size_t n +) { + if (n == 0) { return 0; } - size_t r = a * b; - if(r / b == a) { - *result = r; - return 0; + + char *lbuf; + size_t ncp = 0; + + if (buf) { + if (bufsize == 0) return 0; + lbuf = buf; } else { - *result = 0; - return 1; + if (bufsize == 0) bufsize = CX_STREAM_BCOPY_BUF_SIZE; + lbuf = malloc(bufsize); + if (lbuf == NULL) { + return 0; + } + } + + size_t r; + size_t rn = bufsize > n ? n : bufsize; + while ((r = rfnc(lbuf, 1, rn, src)) != 0) { + r = wfnc(lbuf, 1, r, dest); + ncp += r; + n -= r; + rn = bufsize > n ? n : bufsize; + if (r == 0 || n == 0) { + break; + } + } + + if (lbuf != buf) { + free(lbuf); } + + return ncp; } + +size_t cx_stream_ncopy( + void *src, + void *dest, + cx_read_func rfnc, + cx_write_func wfnc, + size_t n +) { + if (n == 0) { + return 0; + } + + const size_t bufsize = CX_STREAM_COPY_BUF_SIZE; + char lbuf[bufsize]; + size_t ncp = 0; + + size_t r; + size_t rn = bufsize > n ? n : bufsize; + while ((r = rfnc(lbuf, 1, rn, src)) != 0) { + r = wfnc(lbuf, 1, r, dest); + ncp += r; + n -= r; + rn = bufsize > n ? n : bufsize; + if (r == 0 || n == 0) { + break; + } + } + + return ncp; +} + +#ifndef CX_SZMUL_BUILTIN +#include "szmul.c" #endif
--- a/tests/test_utils.cpp Mon Apr 03 19:09:31 2023 +0200 +++ b/tests/test_utils.cpp Mon Apr 03 19:20:30 2023 +0200 @@ -28,8 +28,70 @@ #include "cx/utils.h" +#include "cx/buffer.h" + #include <gtest/gtest.h> +TEST(Utils, cx_stream_bncopy) { + CxBuffer source, target; + char sbuf[32], tbuf[32]; + memset(tbuf, 0, 32); + cxBufferInit(&source, sbuf, 32, nullptr, 0); + cxBufferInit(&target, tbuf, 32, nullptr, 0); + cxBufferPutString(&source, "This is a stream copy test."); + cxBufferSeek(&source, 0, SEEK_SET); + + char tmp[4]; + size_t result = cx_stream_bncopy(&source, &target, + (cx_read_func) cxBufferRead, + (cx_write_func) cxBufferWrite, + tmp, 4, 20); + EXPECT_EQ(20, result); + EXPECT_EQ(20, target.size); + EXPECT_STREQ("This is a stream cop\0", tbuf); + + result = cx_stream_bcopy(&source, &target, + (cx_read_func) cxBufferRead, + (cx_write_func) cxBufferWrite, + nullptr, 16); + + EXPECT_EQ(7, result); + EXPECT_EQ(27, target.size); + EXPECT_STREQ("This is a stream copy test.\0", tbuf); + + cxBufferDestroy(&source); + cxBufferDestroy(&target); +} + +TEST(Utils, cx_stream_ncopy) { + CxBuffer source, target; + char sbuf[32], tbuf[32]; + memset(tbuf, 0, 32); + cxBufferInit(&source, sbuf, 32, nullptr, 0); + cxBufferInit(&target, tbuf, 32, nullptr, 0); + cxBufferPutString(&source, "This is a stream copy test."); + cxBufferSeek(&source, 0, SEEK_SET); + + size_t result = cx_stream_ncopy(&source, &target, + (cx_read_func) cxBufferRead, + (cx_write_func) cxBufferWrite, + 20); + EXPECT_EQ(20, result); + EXPECT_EQ(20, target.size); + EXPECT_STREQ("This is a stream cop\0", tbuf); + + result = cx_stream_copy(&source, &target, + (cx_read_func) cxBufferRead, + (cx_write_func) cxBufferWrite); + + EXPECT_EQ(7, result); + EXPECT_EQ(27, target.size); + EXPECT_STREQ("This is a stream copy test.\0", tbuf); + + cxBufferDestroy(&source); + cxBufferDestroy(&target); +} + TEST(Utils, ForN) { unsigned j; j = 0; @@ -106,7 +168,7 @@ struct Utils_szmul_impl : ::testing::Test { #undef CX_SZMUL_BUILTIN -#include "../src/utils.c" +#include "../src/szmul.c" #define CX_SZMUL_BUILTIN };