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

mercurial