adds new shift operations for UcxBuffer (including tests and a usage example in modules.md)

Wed, 09 May 2018 20:15:10 +0200

author
Mike Becker <universe@uap-core.de>
date
Wed, 09 May 2018 20:15:10 +0200
changeset 290
d5d6ab809ad3
parent 289
a5eabd407774
child 291
deb0035635eb

adds new shift operations for UcxBuffer (including tests and a usage example in modules.md)

docs/src/modules.md file | annotate | diff | comparison | revisions
src/buffer.c file | annotate | diff | comparison | revisions
src/ucx/buffer.h file | annotate | diff | comparison | revisions
test/buffer_tests.c file | annotate | diff | comparison | revisions
test/buffer_tests.h file | annotate | diff | comparison | revisions
test/main.c file | annotate | diff | comparison | revisions
     1.1 --- a/docs/src/modules.md	Wed May 09 15:04:15 2018 +0200
     1.2 +++ b/docs/src/modules.md	Wed May 09 20:15:10 2018 +0200
     1.3 @@ -108,6 +108,80 @@
     1.4  See the documentation of the macro constants in the header file for more
     1.5  information.
     1.6  
     1.7 +### Add line numbers to a file
     1.8 +
     1.9 +When reading a file line by line, you have three options: first, you could limit
    1.10 +the maximum supported line length.
    1.11 +Second, you allocate a god buffer large
    1.12 +enough for the most lines a text file could have.
    1.13 +And third, undoubtedly the best option, you start with a small buffer, which
    1.14 +adjusts on demand.
    1.15 +An `UcxBuffer` can be created to do just that for you.
    1.16 +Just pass the `UCX_BUFFER_AUTOEXTEND` option to the initialization function.
    1.17 +Here is a full working program, which adds line numbers to a file.
    1.18 +```C
    1.19 +#include <stdio.h>
    1.20 +#include <ucx/buffer.h>
    1.21 +#include <ucx/utils.h>
    1.22 +
    1.23 +int main(int argc, char** argv) {
    1.24 +
    1.25 +    if (argc != 2) {
    1.26 +        fprintf(stderr, "Usage: %s <file>\n", argv[0]);
    1.27 +        return 1;
    1.28 +    }
    1.29 +
    1.30 +    FILE* input = fopen(argv[1], "r");
    1.31 +    if (!input) {
    1.32 +        perror("Canno read input");
    1.33 +        return 1;
    1.34 +    }
    1.35 +
    1.36 +    const size_t chunksize = 256;
    1.37 +
    1.38 +    UcxBuffer* linebuf =
    1.39 +        ucx_buffer_new(
    1.40 +            NULL,       // the buffer should manage the memory area for us
    1.41 +            chunksize,  // initial buffer size should be the chunk size
    1.42 +            UCX_BUFFER_AUTOEXTEND); // the buffer will grow when necessary
    1.43 +
    1.44 +    size_t lineno = 1;
    1.45 +    do {
    1.46 +        // read line chunk
    1.47 +        size_t read = ucx_stream_ncopy(
    1.48 +                input, linebuf, fread, ucx_buffer_write, chunksize);
    1.49 +        if (read == 0) break;
    1.50 +        
    1.51 +        // handle line endings
    1.52 +        do {
    1.53 +            sstr_t bufstr = ucx_buffer_to_sstr(linebuf);
    1.54 +            sstr_t nl = sstrchr(bufstr, '\n');
    1.55 +            if (nl.length == 0) break;
    1.56 +
    1.57 +            size_t linelen = bufstr.length - nl.length;
    1.58 +            sstr_t linestr = sstrsubsl(bufstr, 0, linelen);
    1.59 +
    1.60 +            printf("%zu: %" PRIsstr "\n", lineno++, SFMT(linestr));
    1.61 +
    1.62 +            // shift the buffer to the next line
    1.63 +            ucx_buffer_shift_left(linebuf, linelen+1);
    1.64 +        } while(1);
    1.65 +
    1.66 +    } while(1);
    1.67 +
    1.68 +    // print the 'noeol' line, if any
    1.69 +    sstr_t lastline = ucx_buffer_to_sstr(linebuf);
    1.70 +    if (lastline.length > 0) {
    1.71 +        printf("%zu: %" PRIsstr, lineno, SFMT(lastline));
    1.72 +    }
    1.73 +
    1.74 +    fclose(input);
    1.75 +    ucx_buffer_free(linebuf);
    1.76 +
    1.77 +    return 0;
    1.78 +}
    1.79 +```
    1.80 +
    1.81  ## List
    1.82  
    1.83  *Header file:* [list.h](api/list_8h.html)  
     2.1 --- a/src/buffer.c	Wed May 09 15:04:15 2018 +0200
     2.2 +++ b/src/buffer.c	Wed May 09 20:15:10 2018 +0200
     2.3 @@ -237,6 +237,61 @@
     2.4      }
     2.5  }
     2.6  
     2.7 -size_t ucx_buffer_puts(UcxBuffer *buffer, char *str) {
     2.8 +size_t ucx_buffer_puts(UcxBuffer *buffer, const char *str) {
     2.9      return ucx_buffer_write((const void*)str, 1, strlen(str), buffer);
    2.10  }
    2.11 +
    2.12 +int ucx_buffer_shift_left(UcxBuffer* buffer, size_t shift) {
    2.13 +    if (shift >= buffer->size) {
    2.14 +        buffer->pos = buffer->size = 0;
    2.15 +    } else {
    2.16 +        memmove(buffer->space, buffer->space + shift, buffer->size - shift);
    2.17 +        buffer->size -= shift;
    2.18 +        
    2.19 +        if (buffer->pos >= shift) {
    2.20 +            buffer->pos -= shift;
    2.21 +        } else {
    2.22 +            buffer->pos = 0;
    2.23 +        }
    2.24 +    }
    2.25 +    return 0;
    2.26 +}
    2.27 +
    2.28 +int ucx_buffer_shift_right(UcxBuffer* buffer, size_t shift) {
    2.29 +    size_t req_capacity = buffer->size + shift;
    2.30 +    size_t movebytes;
    2.31 +    
    2.32 +    // auto extend buffer, if required and enabled
    2.33 +    if (buffer->capacity < req_capacity) {
    2.34 +        if ((buffer->flags & UCX_BUFFER_AUTOEXTEND) == UCX_BUFFER_AUTOEXTEND) {
    2.35 +            if (ucx_buffer_extend(buffer, req_capacity - buffer->capacity)) {
    2.36 +                return 1;
    2.37 +            }
    2.38 +            movebytes = buffer->size;
    2.39 +        } else {
    2.40 +            movebytes = buffer->capacity - shift;
    2.41 +        }
    2.42 +    } else {
    2.43 +        movebytes = buffer->size;
    2.44 +    }
    2.45 +    
    2.46 +    memmove(buffer->space + shift, buffer->space, movebytes);
    2.47 +    buffer->size = shift+movebytes;
    2.48 +    
    2.49 +    buffer->pos += shift;
    2.50 +    if (buffer->pos > buffer->size) {
    2.51 +        buffer->pos = buffer->size;
    2.52 +    }
    2.53 +    
    2.54 +    return 0;
    2.55 +}
    2.56 +
    2.57 +int ucx_buffer_shift(UcxBuffer* buffer, off_t shift) {
    2.58 +    if (shift < 0) {
    2.59 +        return ucx_buffer_shift_left(buffer, (size_t) (-shift));
    2.60 +    } else if (shift > 0) {
    2.61 +        return ucx_buffer_shift_right(buffer, (size_t) shift);
    2.62 +    } else {
    2.63 +        return 0;
    2.64 +    }
    2.65 +}
     3.1 --- a/src/ucx/buffer.h	Wed May 09 15:04:15 2018 +0200
     3.2 +++ b/src/ucx/buffer.h	Wed May 09 20:15:10 2018 +0200
     3.3 @@ -137,6 +137,67 @@
     3.4  #define ucx_buffer_clone(src,flags) \
     3.5      ucx_buffer_extract(src, 0, (src)->capacity, flags)
     3.6  
     3.7 +
     3.8 +/**
     3.9 + * Shifts the contents of the buffer by the given offset.
    3.10 + * 
    3.11 + * If the offset is positive, the contents are shifted to the right.
    3.12 + * If auto extension is enabled, the buffer grows, if necessary.
    3.13 + * In case the auto extension fails, this function returns a non-zero value and
    3.14 + * no contents are changed.
    3.15 + * If auto extension is disabled, the contents that do not fit into the buffer
    3.16 + * are discarded.
    3.17 + * 
    3.18 + * If the offset is negative, the contents are shifted to the left where the
    3.19 + * first <code>shift</code> bytes are discarded.
    3.20 + * The new size of the buffer is the old size minus
    3.21 + * the absolute shift value.
    3.22 + * If this value is larger than the buffer size, the buffer is emptied (but
    3.23 + * not cleared, see the security note below).
    3.24 + * 
    3.25 + * The buffer position gets shifted alongside with the content but is kept
    3.26 + * within the boundaries of the buffer.
    3.27 + * 
    3.28 + * <b>Security note:</b> the shifting operation does <em>not</em> erase the
    3.29 + * previously occupied memory cells. You can easily do that manually, e.g. by
    3.30 + * calling <code>memset(buffer->space, 0, shift)</code> for a right shift or
    3.31 + * <code>memset(buffer->size, 0, buffer->capacity-buffer->size)</code>
    3.32 + * for a left shift.
    3.33 + * 
    3.34 + * @param buffer the buffer
    3.35 + * @param shift the shift offset (negative means left shift)
    3.36 + * @return 0 on success, non-zero if a required auto-extension fails
    3.37 + */
    3.38 +int ucx_buffer_shift(UcxBuffer* buffer, off_t shift);
    3.39 +
    3.40 +/**
    3.41 + * Shifts the buffer to the right.
    3.42 + * See ucx_buffer_shift() for details.
    3.43 + * 
    3.44 + * @param buffer the buffer
    3.45 + * @param shift the shift offset
    3.46 + * @return 0 on success, non-zero if a required auto-extension fails
    3.47 + * @see ucx_buffer_shift()
    3.48 + */
    3.49 +int ucx_buffer_shift_right(UcxBuffer* buffer, size_t shift);
    3.50 +
    3.51 +/**
    3.52 + * Shifts the buffer to the left.
    3.53 + * 
    3.54 + * See ucx_buffer_shift() for details. Note, however, that this method expects
    3.55 + * a positive shift offset.
    3.56 + * 
    3.57 + * Since a left shift cannot fail due to memory allocation problems, this
    3.58 + * function always returns zero.
    3.59 + * 
    3.60 + * @param buffer the buffer
    3.61 + * @param shift the shift offset
    3.62 + * @return always zero
    3.63 + * @see ucx_buffer_shift()
    3.64 + */
    3.65 +int ucx_buffer_shift_left(UcxBuffer* buffer, size_t shift);
    3.66 +
    3.67 +
    3.68  /**
    3.69   * Moves the position of the buffer.
    3.70   * 
    3.71 @@ -260,7 +321,7 @@
    3.72   * @param str the string
    3.73   * @return the number of bytes written
    3.74   */
    3.75 -size_t ucx_buffer_puts(UcxBuffer *buffer, char *str);
    3.76 +size_t ucx_buffer_puts(UcxBuffer *buffer, const char *str);
    3.77  
    3.78  /**
    3.79   * Returns the complete buffer content as sstr_t.
     4.1 --- a/test/buffer_tests.c	Wed May 09 15:04:15 2018 +0200
     4.2 +++ b/test/buffer_tests.c	Wed May 09 20:15:10 2018 +0200
     4.3 @@ -625,3 +625,114 @@
     4.4      
     4.5      ucx_buffer_free(b);
     4.6  }
     4.7 +
     4.8 +UCX_TEST(test_ucx_buffer_shl) {
     4.9 +    
    4.10 +    const char* hw = "Shift the World!";
    4.11 +    
    4.12 +    UcxBuffer *b = ucx_buffer_new(NULL, 20, UCX_BUFFER_DEFAULT);
    4.13 +    ucx_buffer_puts(b, hw);
    4.14 +    b->pos = 5;
    4.15 +    
    4.16 +    UCX_TEST_BEGIN
    4.17 +    char* expected;
    4.18 +    
    4.19 +    ucx_buffer_shift_left(b, 2);
    4.20 +    expected = "ift the World!";
    4.21 +    
    4.22 +    UCX_TEST_ASSERT(b->pos == 3, "position after normal shl wrong");
    4.23 +    UCX_TEST_ASSERT(b->size == strlen(expected), "size after normal shl wrong");
    4.24 +    UCX_TEST_ASSERT(!memcmp(b->space, expected, b->size),
    4.25 +            "contents after normal shl wrong");
    4.26 +    
    4.27 +    
    4.28 +    ucx_buffer_shift_left(b, 5);
    4.29 +    expected = "he World!";
    4.30 +    
    4.31 +    UCX_TEST_ASSERT(b->pos == 0, "position after overshift left wrong");
    4.32 +    UCX_TEST_ASSERT(b->size == strlen(expected),
    4.33 +            "size after overshift left wrong");
    4.34 +    UCX_TEST_ASSERT(!memcmp(b->space, expected, b->size),
    4.35 +            "contents after overshift left wrong");
    4.36 +    
    4.37 +    ucx_buffer_shift_left(b, 10);
    4.38 +    UCX_TEST_ASSERT(b->pos == 0, "position after 'shl everything away' wrong");
    4.39 +    UCX_TEST_ASSERT(b->size == 0, "size after 'shl everything away' wrong");
    4.40 +    
    4.41 +    UCX_TEST_END
    4.42 +    
    4.43 +    ucx_buffer_free(b);    
    4.44 +}
    4.45 +
    4.46 +UCX_TEST(test_ucx_buffer_shr) {
    4.47 +    
    4.48 +    const char* hw = "Shift the World!";
    4.49 +    
    4.50 +    UcxBuffer *b = ucx_buffer_new(NULL, 20, UCX_BUFFER_DEFAULT);
    4.51 +    ucx_buffer_puts(b, hw);
    4.52 +    b->pos = 12;
    4.53 +    
    4.54 +    UCX_TEST_BEGIN
    4.55 +    char* expected;
    4.56 +    
    4.57 +    ucx_buffer_shift_right(b, 2);
    4.58 +    expected = "ShShift the World!";
    4.59 +    
    4.60 +    UCX_TEST_ASSERT(b->pos == 14, "position after normal shr wrong");
    4.61 +    UCX_TEST_ASSERT(b->size == strlen(expected), "size after normal shr wrong");
    4.62 +    UCX_TEST_ASSERT(!memcmp(b->space, expected, b->size),
    4.63 +            "contents after normal shr wrong");
    4.64 +    
    4.65 +    
    4.66 +    ucx_buffer_shift_right(b, 5);
    4.67 +    expected = "ShShiShShift the Wor";
    4.68 +    UCX_TEST_ASSERT(strlen(expected) == b->capacity,
    4.69 +            "Test data is wrong, please fix the test.");
    4.70 +    
    4.71 +    UCX_TEST_ASSERT(b->pos == 19,
    4.72 +            "position after overshift right w/o auto-extend wrong");
    4.73 +    UCX_TEST_ASSERT(b->size == 20,
    4.74 +            "size after overshift right w/o auto-extend wrong");
    4.75 +    UCX_TEST_ASSERT(b->capacity == 20,
    4.76 +            "capacity after overshift right w/o auto-extend wrong");
    4.77 +    UCX_TEST_ASSERT(!memcmp(b->space, expected, b->size),
    4.78 +            "contents after overshift right w/o auto-extend wrong");
    4.79 +    
    4.80 +    ucx_buffer_shift_right(b, 15);
    4.81 +    UCX_TEST_ASSERT(b->pos == b->capacity, "position after 'shr to eof' wrong");
    4.82 +    UCX_TEST_ASSERT(b->size == b->capacity, "size after 'shr to eof' wrong");
    4.83 +    UCX_TEST_ASSERT(ucx_buffer_eof(b), "buffer not eof after 'shr to eof'");
    4.84 +    
    4.85 +    UCX_TEST_END
    4.86 +    
    4.87 +    ucx_buffer_free(b);    
    4.88 +}
    4.89 +
    4.90 +UCX_TEST(test_ucx_buffer_shr_ax) {
    4.91 +    
    4.92 +    const char* hw = "Shift the World!";
    4.93 +    
    4.94 +    UcxBuffer *b = ucx_buffer_new(NULL, 20, UCX_BUFFER_AUTOEXTEND);
    4.95 +    ucx_buffer_puts(b, hw);
    4.96 +    b->pos = 12;
    4.97 +    
    4.98 +    UCX_TEST_BEGIN
    4.99 +    
   4.100 +    const char* expected = "Shift the Shift the World!";
   4.101 +    
   4.102 +    ucx_buffer_shift_right(b, 10);
   4.103 +    UCX_TEST_ASSERT(b->pos == 22, "position after shr w/ auto-extend wrong");
   4.104 +    UCX_TEST_ASSERT(b->size == strlen(expected),
   4.105 +            "size after shr w/ auto-extend wrong");
   4.106 +    UCX_TEST_ASSERT(b->capacity >= b->size,
   4.107 +            "auto-extension of capacity after shr w/ auto-extend failed");
   4.108 +    UCX_TEST_ASSERT(!ucx_buffer_eof(b),
   4.109 +            "buffer should not be eof after shr w/ auto-extend");
   4.110 +    UCX_TEST_ASSERT(!memcmp(b->space, expected, b->size),
   4.111 +            "contents wrong after shr w/ auto-extend");
   4.112 +    
   4.113 +    UCX_TEST_END
   4.114 +    
   4.115 +    ucx_buffer_free(b);    
   4.116 +}
   4.117 +
     5.1 --- a/test/buffer_tests.h	Wed May 09 15:04:15 2018 +0200
     5.2 +++ b/test/buffer_tests.h	Wed May 09 20:15:10 2018 +0200
     5.3 @@ -60,6 +60,9 @@
     5.4  UCX_TEST(test_ucx_buffer_write);
     5.5  UCX_TEST(test_ucx_buffer_write_oob);
     5.6  UCX_TEST(test_ucx_buffer_write_ax);
     5.7 +UCX_TEST(test_ucx_buffer_shl);
     5.8 +UCX_TEST(test_ucx_buffer_shr);
     5.9 +UCX_TEST(test_ucx_buffer_shr_ax);
    5.10  
    5.11  
    5.12  #ifdef	__cplusplus
     6.1 --- a/test/main.c	Wed May 09 15:04:15 2018 +0200
     6.2 +++ b/test/main.c	Wed May 09 20:15:10 2018 +0200
     6.3 @@ -223,6 +223,9 @@
     6.4          ucx_test_register(suite, test_ucx_buffer_write);
     6.5          ucx_test_register(suite, test_ucx_buffer_write_oob);
     6.6          ucx_test_register(suite, test_ucx_buffer_write_ax);
     6.7 +        ucx_test_register(suite, test_ucx_buffer_shl);
     6.8 +        ucx_test_register(suite, test_ucx_buffer_shr);
     6.9 +        ucx_test_register(suite, test_ucx_buffer_shr_ax);
    6.10          
    6.11          /* Utils Tests*/
    6.12          ucx_test_register(suite, test_ucx_fprintf);

mercurial