#184 start implementation of the flush feature

Sat, 30 Apr 2022 09:47:20 +0200

author
Mike Becker <universe@uap-core.de>
date
Sat, 30 Apr 2022 09:47:20 +0200
changeset 539
9cd98da9ee17
parent 538
2cfbcbe86a7c
child 540
47e0f2237a94

#184 start implementation of the flush feature

src/buffer.c file | annotate | diff | comparison | revisions
src/cx/buffer.h file | annotate | diff | comparison | revisions
test/test_buffer.cpp file | annotate | diff | comparison | revisions
     1.1 --- a/src/buffer.c	Sat Apr 30 09:03:17 2022 +0200
     1.2 +++ b/src/buffer.c	Sat Apr 30 09:47:20 2022 +0200
     1.3 @@ -32,6 +32,7 @@
     1.4  #include <stdlib.h>
     1.5  #include <stdio.h>
     1.6  #include <string.h>
     1.7 +#include <stdint.h>
     1.8  
     1.9  int cxBufferInit(
    1.10          CxBuffer *buffer,
    1.11 @@ -53,8 +54,12 @@
    1.12      }
    1.13      buffer->capacity = capacity;
    1.14      buffer->size = 0;
    1.15 +    buffer->pos = 0;
    1.16  
    1.17 -    buffer->pos = 0;
    1.18 +    buffer->flush_func = NULL;
    1.19 +    buffer->flush_blkmax = 0;
    1.20 +    buffer->flush_blksize = 4096;
    1.21 +    buffer->flush_threshold = SIZE_MAX;
    1.22  
    1.23      return 0;
    1.24  }
    1.25 @@ -134,6 +139,7 @@
    1.26          size_t nitems,
    1.27          CxBuffer *buffer
    1.28  ) {
    1.29 +    // TODO: optimize for special case size == nitems == 1
    1.30      size_t len;
    1.31      if (cx_szmul(size, nitems, &len)) {
    1.32          return 0;
    1.33 @@ -143,15 +149,27 @@
    1.34          return 0;
    1.35      }
    1.36  
    1.37 +    bool perform_flush = false;
    1.38      if (required > buffer->capacity) {
    1.39 -        if ((buffer->flags & CX_BUFFER_AUTO_EXTEND) == CX_BUFFER_AUTO_EXTEND) {
    1.40 -            if (cxBufferMinimumCapacity(buffer, required)) {
    1.41 -                return 0;
    1.42 +        if ((buffer->flags & CX_BUFFER_AUTO_EXTEND) == CX_BUFFER_AUTO_EXTEND && required) {
    1.43 +            if (buffer->flush_blkmax > 0 && required > buffer->flush_threshold) {
    1.44 +                perform_flush = true;
    1.45 +            } else {
    1.46 +                if (cxBufferMinimumCapacity(buffer, required)) {
    1.47 +                    return 0;
    1.48 +                }
    1.49              }
    1.50          } else {
    1.51 -            len = buffer->capacity - buffer->pos;
    1.52 -            if (size > 1) {
    1.53 -                len -= len % size;
    1.54 +            if (buffer->flush_blkmax > 0) {
    1.55 +                perform_flush = true;
    1.56 +            } else {
    1.57 +                // truncate data to be written, if we can neither extend nor flush
    1.58 +                len = buffer->capacity - buffer->pos;
    1.59 +                if (size > 1) {
    1.60 +                    // TODO: this is bugged - it would only discard one element and not as many as required
    1.61 +                    len -= len % size;
    1.62 +                    nitems = len / size;
    1.63 +                }
    1.64              }
    1.65          }
    1.66      }
    1.67 @@ -160,13 +178,25 @@
    1.68          return len;
    1.69      }
    1.70  
    1.71 -    memcpy(buffer->bytes + buffer->pos, ptr, len);
    1.72 -    buffer->pos += len;
    1.73 -    if (buffer->pos > buffer->size) {
    1.74 -        buffer->size = buffer->pos;
    1.75 +    if (perform_flush) {
    1.76 +        // TODO: implement flushing
    1.77 +        // (1) determine how many bytes to flush (use flushmax = blkmax * blksize)
    1.78 +        // (2) if len is larger than the number computed in (1) we need more flush cycles, compute how many
    1.79 +        // (3) determine how many bytes from the buffer shall be flushed
    1.80 +        // (4) if something remains in the buffer, shift the buffer to the left
    1.81 +        // (4a) if buffer was shifted, append the new data to the buffer
    1.82 +        // (4b) if the buffer was flushed entirely AND the new data also fits into flushmax,
    1.83 +        //      directly write the new data to the flush sink
    1.84 +        return 0; // remove this after implementation
    1.85 +    } else {
    1.86 +        memcpy(buffer->bytes + buffer->pos, ptr, len);
    1.87 +        buffer->pos += len;
    1.88 +        if (buffer->pos > buffer->size) {
    1.89 +            buffer->size = buffer->pos;
    1.90 +        }
    1.91      }
    1.92  
    1.93 -    return len / size;
    1.94 +    return nitems;
    1.95  }
    1.96  
    1.97  int cxBufferPut(
     2.1 --- a/src/cx/buffer.h	Sat Apr 30 09:03:17 2022 +0200
     2.2 +++ b/src/cx/buffer.h	Sat Apr 30 09:47:20 2022 +0200
     2.3 @@ -91,6 +91,40 @@
     2.4      /** Current size of the buffer content. */
     2.5      size_t size;
     2.6      /**
     2.7 +     * The buffer may not extend beyond this threshold before starting to flush.
     2.8 +     * Default is \c SIZE_MAX (flushing disabled when auto extension is enabled).
     2.9 +     */
    2.10 +    size_t flush_threshold;
    2.11 +    /**
    2.12 +     * The block size for the elements to flush.
    2.13 +     * Default is 4096 bytes.
    2.14 +     */
    2.15 +    size_t flush_blksize;
    2.16 +    /**
    2.17 +     * The maximum number of blocks to flush in one cycle.
    2.18 +     * Zero disables flushing entirely (this is the default).
    2.19 +     * Set this to \c SIZE_MAX to flush the entire buffer.
    2.20 +     *
    2.21 +     * @attention if the maximum number of blocks multiplied with the block size
    2.22 +     * is smaller than the expected contents written to this buffer within one write
    2.23 +     * operation, multiple flush cycles are performed after that write.
    2.24 +     * That means the total number of blocks flushed after one write to this buffer may
    2.25 +     * be larger than \c flush_blkmax.
    2.26 +     */
    2.27 +    size_t flush_blkmax;
    2.28 +
    2.29 +    /**
    2.30 +     * The write function used for flushing.
    2.31 +     * If NULL, the flushed content gets discarded.
    2.32 +     */
    2.33 +    size_t (*flush_func)(
    2.34 +            void const *,
    2.35 +            size_t,
    2.36 +            size_t,
    2.37 +            void *
    2.38 +    );
    2.39 +
    2.40 +    /**
    2.41       * Flag register for buffer features.
    2.42       * @see #CX_BUFFER_DEFAULT
    2.43       * @see #CX_BUFFER_FREE_CONTENTS
     3.1 --- a/test/test_buffer.cpp	Sat Apr 30 09:03:17 2022 +0200
     3.2 +++ b/test/test_buffer.cpp	Sat Apr 30 09:47:20 2022 +0200
     3.3 @@ -31,11 +31,19 @@
     3.4  #include <gtest/gtest.h>
     3.5  #include "util_allocator.h"
     3.6  
     3.7 +static void expect_default_flush_config(CxBuffer *buf) {
     3.8 +    EXPECT_EQ(buf->flush_blkmax, 0);
     3.9 +    EXPECT_EQ(buf->flush_blksize, 4096);
    3.10 +    EXPECT_EQ(buf->flush_threshold, SIZE_MAX);
    3.11 +    EXPECT_EQ(buf->flush_func, nullptr);
    3.12 +}
    3.13 +
    3.14  TEST(BufferInit, WrapSpace) {
    3.15      CxTestingAllocator alloc;
    3.16      CxBuffer buf;
    3.17      void *space = cxMalloc(&alloc, 16);
    3.18      cxBufferInit(&buf, space, 16, &alloc, CX_BUFFER_DEFAULT);
    3.19 +    expect_default_flush_config(&buf);
    3.20      EXPECT_EQ(buf.space, space);
    3.21      EXPECT_EQ(buf.flags & CX_BUFFER_AUTO_EXTEND, 0);
    3.22      EXPECT_EQ(buf.flags & CX_BUFFER_FREE_CONTENTS, 0);
    3.23 @@ -49,11 +57,31 @@
    3.24      EXPECT_TRUE(alloc.verify());
    3.25  }
    3.26  
    3.27 +TEST(BufferInit, WrapSpaceAutoExtend) {
    3.28 +    CxTestingAllocator alloc;
    3.29 +    CxBuffer buf;
    3.30 +    void *space = cxMalloc(&alloc, 16);
    3.31 +    cxBufferInit(&buf, space, 16, &alloc, CX_BUFFER_AUTO_EXTEND);
    3.32 +    expect_default_flush_config(&buf);
    3.33 +    EXPECT_EQ(buf.space, space);
    3.34 +    EXPECT_EQ(buf.flags & CX_BUFFER_AUTO_EXTEND, CX_BUFFER_AUTO_EXTEND);
    3.35 +    EXPECT_EQ(buf.flags & CX_BUFFER_FREE_CONTENTS, 0);
    3.36 +    EXPECT_EQ(buf.pos, 0);
    3.37 +    EXPECT_EQ(buf.size, 0);
    3.38 +    EXPECT_EQ(buf.capacity, 16);
    3.39 +    EXPECT_EQ(buf.allocator, &alloc);
    3.40 +    cxBufferDestroy(&buf);
    3.41 +    EXPECT_FALSE(alloc.verify());
    3.42 +    cxFree(&alloc, space);
    3.43 +    EXPECT_TRUE(alloc.verify());
    3.44 +}
    3.45 +
    3.46  TEST(BufferInit, WrapSpaceAutoFree) {
    3.47      CxTestingAllocator alloc;
    3.48      CxBuffer buf;
    3.49      void *space = cxMalloc(&alloc, 16);
    3.50      cxBufferInit(&buf, space, 16, &alloc, CX_BUFFER_FREE_CONTENTS);
    3.51 +    expect_default_flush_config(&buf);
    3.52      EXPECT_EQ(buf.space, space);
    3.53      EXPECT_EQ(buf.flags & CX_BUFFER_AUTO_EXTEND, 0);
    3.54      EXPECT_EQ(buf.flags & CX_BUFFER_FREE_CONTENTS, CX_BUFFER_FREE_CONTENTS);
    3.55 @@ -70,6 +98,7 @@
    3.56      CxTestingAllocator alloc;
    3.57      CxBuffer buf;
    3.58      cxBufferInit(&buf, nullptr, 8, &alloc, CX_BUFFER_DEFAULT);
    3.59 +    expect_default_flush_config(&buf);
    3.60      EXPECT_NE(buf.space, nullptr);
    3.61      EXPECT_EQ(buf.flags & CX_BUFFER_AUTO_EXTEND, 0);
    3.62      EXPECT_EQ(buf.flags & CX_BUFFER_FREE_CONTENTS, CX_BUFFER_FREE_CONTENTS);

mercurial