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 |
1.1 --- a/src/cx/common.h Mon Apr 03 19:09:31 2023 +0200 1.2 +++ b/src/cx/common.h Mon Apr 03 19:20:30 2023 +0200 1.3 @@ -104,6 +104,16 @@ 1.4 void * 1.5 ); 1.6 1.7 +/** 1.8 + * Function pointer compatible with fread-like functions. 1.9 + */ 1.10 +typedef size_t (*cx_read_func)( 1.11 + void *, 1.12 + size_t, 1.13 + size_t, 1.14 + void * 1.15 +); 1.16 + 1.17 #ifdef _WIN32 1.18 1.19 #ifdef __MINGW32__
2.1 --- a/src/cx/utils.h Mon Apr 03 19:09:31 2023 +0200 2.2 +++ b/src/cx/utils.h Mon Apr 03 19:20:30 2023 +0200 2.3 @@ -107,7 +107,87 @@ 2.4 */ 2.5 int cx_szmul_impl(size_t a, size_t b, size_t *result); 2.6 2.7 -#endif 2.8 +#endif // cx_szmul 2.9 + 2.10 + 2.11 +/** 2.12 + * Reads data from a stream and writes it to another stream. 2.13 + * 2.14 + * @param src the source stream 2.15 + * @param dest the destination stream 2.16 + * @param rfnc the read function 2.17 + * @param wfnc the write function 2.18 + * @param buf a pointer to the copy buffer or \c NULL if a buffer 2.19 + * shall be implicitly created on the heap 2.20 + * @param bufsize the size of the copy buffer - if \p buf is \c NULL you can 2.21 + * set this to zero to let the implementation decide 2.22 + * @param n the maximum number of bytes that shall be copied. 2.23 + * If this is larger than \p bufsize, the content is copied over multiple 2.24 + * iterations. 2.25 + * @return the total number of bytes copied 2.26 + */ 2.27 +__attribute__((__nonnull__(1, 2, 3, 4))) 2.28 +size_t cx_stream_bncopy( 2.29 + void *src, 2.30 + void *dest, 2.31 + cx_read_func rfnc, 2.32 + cx_write_func wfnc, 2.33 + char *buf, 2.34 + size_t bufsize, 2.35 + size_t n 2.36 +); 2.37 + 2.38 +/** 2.39 + * Reads data from a stream and writes it to another stream. 2.40 + * 2.41 + * @param src the source stream 2.42 + * @param dest the destination stream 2.43 + * @param rfnc the read function 2.44 + * @param wfnc the write function 2.45 + * @param buf a pointer to the copy buffer or \c NULL if a buffer 2.46 + * shall be implicitly created on the heap 2.47 + * @param bufsize the size of the copy buffer - if \p buf is \c NULL you can 2.48 + * set this to zero to let the implementation decide 2.49 + * @return total number of bytes copied 2.50 + */ 2.51 +#define cx_stream_bcopy(src, dest, rfnc, wfnc, buf, bufsize) \ 2.52 + cx_stream_bncopy(src, dest, rfnc, wfnc, buf, bufsize, SIZE_MAX) 2.53 + 2.54 +/** 2.55 + * Reads data from a stream and writes it to another stream. 2.56 + * 2.57 + * The data is temporarily stored in a stack allocated buffer. 2.58 + * 2.59 + * @param src the source stream 2.60 + * @param dest the destination stream 2.61 + * @param rfnc the read function 2.62 + * @param wfnc the write function 2.63 + * @param n the maximum number of bytes that shall be copied. 2.64 + * @return total number of bytes copied 2.65 + */ 2.66 +__attribute__((__nonnull__)) 2.67 +size_t cx_stream_ncopy( 2.68 + void *src, 2.69 + void *dest, 2.70 + cx_read_func rfnc, 2.71 + cx_write_func wfnc, 2.72 + size_t n 2.73 +); 2.74 + 2.75 +/** 2.76 + * Reads data from a stream and writes it to another stream. 2.77 + * 2.78 + * The data is temporarily stored in a stack allocated buffer. 2.79 + * 2.80 + * @param src the source stream 2.81 + * @param dest the destination stream 2.82 + * @param rfnc the read function 2.83 + * @param wfnc the write function 2.84 + * @param n the maximum number of bytes that shall be copied. 2.85 + * @return total number of bytes copied 2.86 + */ 2.87 +#define cx_stream_copy(src, dest, rfnc, wfnc) \ 2.88 + cx_stream_ncopy(src, dest, rfnc, wfnc, SIZE_MAX) 2.89 2.90 #ifdef __cplusplus 2.91 }
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 3.2 +++ b/src/szmul.c Mon Apr 03 19:20:30 2023 +0200 3.3 @@ -0,0 +1,46 @@ 3.4 +/* 3.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3.6 + * 3.7 + * Copyright 2023 Mike Becker, Olaf Wintermann All rights reserved. 3.8 + * 3.9 + * Redistribution and use in source and binary forms, with or without 3.10 + * modification, are permitted provided that the following conditions are met: 3.11 + * 3.12 + * 1. Redistributions of source code must retain the above copyright 3.13 + * notice, this list of conditions and the following disclaimer. 3.14 + * 3.15 + * 2. Redistributions in binary form must reproduce the above copyright 3.16 + * notice, this list of conditions and the following disclaimer in the 3.17 + * documentation and/or other materials provided with the distribution. 3.18 + * 3.19 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 3.20 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 3.21 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 3.22 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 3.23 + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 3.24 + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 3.25 + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 3.26 + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 3.27 + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 3.28 + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 3.29 + * POSSIBILITY OF SUCH DAMAGE. 3.30 + */ 3.31 + 3.32 +int cx_szmul_impl( 3.33 + size_t a, 3.34 + size_t b, 3.35 + size_t *result 3.36 +) { 3.37 + if (a == 0 || b == 0) { 3.38 + *result = 0; 3.39 + return 0; 3.40 + } 3.41 + size_t r = a * b; 3.42 + if (r / b == a) { 3.43 + *result = r; 3.44 + return 0; 3.45 + } else { 3.46 + *result = 0; 3.47 + return 1; 3.48 + } 3.49 +} 3.50 \ No newline at end of file
4.1 --- a/src/utils.c Mon Apr 03 19:09:31 2023 +0200 4.2 +++ b/src/utils.c Mon Apr 03 19:20:30 2023 +0200 4.3 @@ -28,19 +28,85 @@ 4.4 4.5 #include "cx/utils.h" 4.6 4.7 -#ifndef CX_SZMUL_BUILTIN 4.8 -int cx_szmul_impl(size_t a, size_t b, size_t *result) { 4.9 - if(a == 0 || b == 0) { 4.10 - *result = 0; 4.11 +#define CX_STREAM_BCOPY_BUF_SIZE 8192 4.12 +#define CX_STREAM_COPY_BUF_SIZE 1024 4.13 + 4.14 +size_t cx_stream_bncopy( 4.15 + void *src, 4.16 + void *dest, 4.17 + cx_read_func rfnc, 4.18 + cx_write_func wfnc, 4.19 + char *buf, 4.20 + size_t bufsize, 4.21 + size_t n 4.22 +) { 4.23 + if (n == 0) { 4.24 return 0; 4.25 } 4.26 - size_t r = a * b; 4.27 - if(r / b == a) { 4.28 - *result = r; 4.29 + 4.30 + char *lbuf; 4.31 + size_t ncp = 0; 4.32 + 4.33 + if (buf) { 4.34 + if (bufsize == 0) return 0; 4.35 + lbuf = buf; 4.36 + } else { 4.37 + if (bufsize == 0) bufsize = CX_STREAM_BCOPY_BUF_SIZE; 4.38 + lbuf = malloc(bufsize); 4.39 + if (lbuf == NULL) { 4.40 + return 0; 4.41 + } 4.42 + } 4.43 + 4.44 + size_t r; 4.45 + size_t rn = bufsize > n ? n : bufsize; 4.46 + while ((r = rfnc(lbuf, 1, rn, src)) != 0) { 4.47 + r = wfnc(lbuf, 1, r, dest); 4.48 + ncp += r; 4.49 + n -= r; 4.50 + rn = bufsize > n ? n : bufsize; 4.51 + if (r == 0 || n == 0) { 4.52 + break; 4.53 + } 4.54 + } 4.55 + 4.56 + if (lbuf != buf) { 4.57 + free(lbuf); 4.58 + } 4.59 + 4.60 + return ncp; 4.61 +} 4.62 + 4.63 +size_t cx_stream_ncopy( 4.64 + void *src, 4.65 + void *dest, 4.66 + cx_read_func rfnc, 4.67 + cx_write_func wfnc, 4.68 + size_t n 4.69 +) { 4.70 + if (n == 0) { 4.71 return 0; 4.72 - } else { 4.73 - *result = 0; 4.74 - return 1; 4.75 } 4.76 + 4.77 + const size_t bufsize = CX_STREAM_COPY_BUF_SIZE; 4.78 + char lbuf[bufsize]; 4.79 + size_t ncp = 0; 4.80 + 4.81 + size_t r; 4.82 + size_t rn = bufsize > n ? n : bufsize; 4.83 + while ((r = rfnc(lbuf, 1, rn, src)) != 0) { 4.84 + r = wfnc(lbuf, 1, r, dest); 4.85 + ncp += r; 4.86 + n -= r; 4.87 + rn = bufsize > n ? n : bufsize; 4.88 + if (r == 0 || n == 0) { 4.89 + break; 4.90 + } 4.91 + } 4.92 + 4.93 + return ncp; 4.94 } 4.95 + 4.96 +#ifndef CX_SZMUL_BUILTIN 4.97 +#include "szmul.c" 4.98 #endif
5.1 --- a/tests/test_utils.cpp Mon Apr 03 19:09:31 2023 +0200 5.2 +++ b/tests/test_utils.cpp Mon Apr 03 19:20:30 2023 +0200 5.3 @@ -28,8 +28,70 @@ 5.4 5.5 #include "cx/utils.h" 5.6 5.7 +#include "cx/buffer.h" 5.8 + 5.9 #include <gtest/gtest.h> 5.10 5.11 +TEST(Utils, cx_stream_bncopy) { 5.12 + CxBuffer source, target; 5.13 + char sbuf[32], tbuf[32]; 5.14 + memset(tbuf, 0, 32); 5.15 + cxBufferInit(&source, sbuf, 32, nullptr, 0); 5.16 + cxBufferInit(&target, tbuf, 32, nullptr, 0); 5.17 + cxBufferPutString(&source, "This is a stream copy test."); 5.18 + cxBufferSeek(&source, 0, SEEK_SET); 5.19 + 5.20 + char tmp[4]; 5.21 + size_t result = cx_stream_bncopy(&source, &target, 5.22 + (cx_read_func) cxBufferRead, 5.23 + (cx_write_func) cxBufferWrite, 5.24 + tmp, 4, 20); 5.25 + EXPECT_EQ(20, result); 5.26 + EXPECT_EQ(20, target.size); 5.27 + EXPECT_STREQ("This is a stream cop\0", tbuf); 5.28 + 5.29 + result = cx_stream_bcopy(&source, &target, 5.30 + (cx_read_func) cxBufferRead, 5.31 + (cx_write_func) cxBufferWrite, 5.32 + nullptr, 16); 5.33 + 5.34 + EXPECT_EQ(7, result); 5.35 + EXPECT_EQ(27, target.size); 5.36 + EXPECT_STREQ("This is a stream copy test.\0", tbuf); 5.37 + 5.38 + cxBufferDestroy(&source); 5.39 + cxBufferDestroy(&target); 5.40 +} 5.41 + 5.42 +TEST(Utils, cx_stream_ncopy) { 5.43 + CxBuffer source, target; 5.44 + char sbuf[32], tbuf[32]; 5.45 + memset(tbuf, 0, 32); 5.46 + cxBufferInit(&source, sbuf, 32, nullptr, 0); 5.47 + cxBufferInit(&target, tbuf, 32, nullptr, 0); 5.48 + cxBufferPutString(&source, "This is a stream copy test."); 5.49 + cxBufferSeek(&source, 0, SEEK_SET); 5.50 + 5.51 + size_t result = cx_stream_ncopy(&source, &target, 5.52 + (cx_read_func) cxBufferRead, 5.53 + (cx_write_func) cxBufferWrite, 5.54 + 20); 5.55 + EXPECT_EQ(20, result); 5.56 + EXPECT_EQ(20, target.size); 5.57 + EXPECT_STREQ("This is a stream cop\0", tbuf); 5.58 + 5.59 + result = cx_stream_copy(&source, &target, 5.60 + (cx_read_func) cxBufferRead, 5.61 + (cx_write_func) cxBufferWrite); 5.62 + 5.63 + EXPECT_EQ(7, result); 5.64 + EXPECT_EQ(27, target.size); 5.65 + EXPECT_STREQ("This is a stream copy test.\0", tbuf); 5.66 + 5.67 + cxBufferDestroy(&source); 5.68 + cxBufferDestroy(&target); 5.69 +} 5.70 + 5.71 TEST(Utils, ForN) { 5.72 unsigned j; 5.73 j = 0; 5.74 @@ -106,7 +168,7 @@ 5.75 struct Utils_szmul_impl : ::testing::Test { 5.76 #undef CX_SZMUL_BUILTIN 5.77 5.78 -#include "../src/utils.c" 5.79 +#include "../src/szmul.c" 5.80 5.81 #define CX_SZMUL_BUILTIN 5.82 };