add stream copy utils - fixes #254

Mon, 03 Apr 2023 19:20:30 +0200

author
Mike Becker <universe@uap-core.de>
date
Mon, 03 Apr 2023 19:20:30 +0200
changeset 674
dc514a5d42a5
parent 673
60fb6aec157d
child 675
765cf785b7fa

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
 };

mercurial