Wed, 09 May 2018 20:15:10 +0200
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 |
--- a/docs/src/modules.md Wed May 09 15:04:15 2018 +0200 +++ b/docs/src/modules.md Wed May 09 20:15:10 2018 +0200 @@ -108,6 +108,80 @@ See the documentation of the macro constants in the header file for more information. +### Add line numbers to a file + +When reading a file line by line, you have three options: first, you could limit +the maximum supported line length. +Second, you allocate a god buffer large +enough for the most lines a text file could have. +And third, undoubtedly the best option, you start with a small buffer, which +adjusts on demand. +An `UcxBuffer` can be created to do just that for you. +Just pass the `UCX_BUFFER_AUTOEXTEND` option to the initialization function. +Here is a full working program, which adds line numbers to a file. +```C +#include <stdio.h> +#include <ucx/buffer.h> +#include <ucx/utils.h> + +int main(int argc, char** argv) { + + if (argc != 2) { + fprintf(stderr, "Usage: %s <file>\n", argv[0]); + return 1; + } + + FILE* input = fopen(argv[1], "r"); + if (!input) { + perror("Canno read input"); + return 1; + } + + const size_t chunksize = 256; + + UcxBuffer* linebuf = + ucx_buffer_new( + NULL, // the buffer should manage the memory area for us + chunksize, // initial buffer size should be the chunk size + UCX_BUFFER_AUTOEXTEND); // the buffer will grow when necessary + + size_t lineno = 1; + do { + // read line chunk + size_t read = ucx_stream_ncopy( + input, linebuf, fread, ucx_buffer_write, chunksize); + if (read == 0) break; + + // handle line endings + do { + sstr_t bufstr = ucx_buffer_to_sstr(linebuf); + sstr_t nl = sstrchr(bufstr, '\n'); + if (nl.length == 0) break; + + size_t linelen = bufstr.length - nl.length; + sstr_t linestr = sstrsubsl(bufstr, 0, linelen); + + printf("%zu: %" PRIsstr "\n", lineno++, SFMT(linestr)); + + // shift the buffer to the next line + ucx_buffer_shift_left(linebuf, linelen+1); + } while(1); + + } while(1); + + // print the 'noeol' line, if any + sstr_t lastline = ucx_buffer_to_sstr(linebuf); + if (lastline.length > 0) { + printf("%zu: %" PRIsstr, lineno, SFMT(lastline)); + } + + fclose(input); + ucx_buffer_free(linebuf); + + return 0; +} +``` + ## List *Header file:* [list.h](api/list_8h.html)
--- a/src/buffer.c Wed May 09 15:04:15 2018 +0200 +++ b/src/buffer.c Wed May 09 20:15:10 2018 +0200 @@ -237,6 +237,61 @@ } } -size_t ucx_buffer_puts(UcxBuffer *buffer, char *str) { +size_t ucx_buffer_puts(UcxBuffer *buffer, const char *str) { return ucx_buffer_write((const void*)str, 1, strlen(str), buffer); } + +int ucx_buffer_shift_left(UcxBuffer* buffer, size_t shift) { + if (shift >= buffer->size) { + buffer->pos = buffer->size = 0; + } else { + memmove(buffer->space, buffer->space + shift, buffer->size - shift); + buffer->size -= shift; + + if (buffer->pos >= shift) { + buffer->pos -= shift; + } else { + buffer->pos = 0; + } + } + return 0; +} + +int ucx_buffer_shift_right(UcxBuffer* buffer, size_t shift) { + size_t req_capacity = buffer->size + shift; + size_t movebytes; + + // auto extend buffer, if required and enabled + if (buffer->capacity < req_capacity) { + if ((buffer->flags & UCX_BUFFER_AUTOEXTEND) == UCX_BUFFER_AUTOEXTEND) { + if (ucx_buffer_extend(buffer, req_capacity - buffer->capacity)) { + return 1; + } + movebytes = buffer->size; + } else { + movebytes = buffer->capacity - shift; + } + } else { + movebytes = buffer->size; + } + + memmove(buffer->space + shift, buffer->space, movebytes); + buffer->size = shift+movebytes; + + buffer->pos += shift; + if (buffer->pos > buffer->size) { + buffer->pos = buffer->size; + } + + return 0; +} + +int ucx_buffer_shift(UcxBuffer* buffer, off_t shift) { + if (shift < 0) { + return ucx_buffer_shift_left(buffer, (size_t) (-shift)); + } else if (shift > 0) { + return ucx_buffer_shift_right(buffer, (size_t) shift); + } else { + return 0; + } +}
--- a/src/ucx/buffer.h Wed May 09 15:04:15 2018 +0200 +++ b/src/ucx/buffer.h Wed May 09 20:15:10 2018 +0200 @@ -137,6 +137,67 @@ #define ucx_buffer_clone(src,flags) \ ucx_buffer_extract(src, 0, (src)->capacity, flags) + +/** + * Shifts the contents of the buffer by the given offset. + * + * If the offset is positive, the contents are shifted to the right. + * If auto extension is enabled, the buffer grows, if necessary. + * In case the auto extension fails, this function returns a non-zero value and + * no contents are changed. + * If auto extension is disabled, the contents that do not fit into the buffer + * are discarded. + * + * If the offset is negative, the contents are shifted to the left where the + * first <code>shift</code> bytes are discarded. + * The new size of the buffer is the old size minus + * the absolute shift value. + * If this value is larger than the buffer size, the buffer is emptied (but + * not cleared, see the security note below). + * + * The buffer position gets shifted alongside with the content but is kept + * within the boundaries of the buffer. + * + * <b>Security note:</b> the shifting operation does <em>not</em> erase the + * previously occupied memory cells. You can easily do that manually, e.g. by + * calling <code>memset(buffer->space, 0, shift)</code> for a right shift or + * <code>memset(buffer->size, 0, buffer->capacity-buffer->size)</code> + * for a left shift. + * + * @param buffer the buffer + * @param shift the shift offset (negative means left shift) + * @return 0 on success, non-zero if a required auto-extension fails + */ +int ucx_buffer_shift(UcxBuffer* buffer, off_t shift); + +/** + * Shifts the buffer to the right. + * See ucx_buffer_shift() for details. + * + * @param buffer the buffer + * @param shift the shift offset + * @return 0 on success, non-zero if a required auto-extension fails + * @see ucx_buffer_shift() + */ +int ucx_buffer_shift_right(UcxBuffer* buffer, size_t shift); + +/** + * Shifts the buffer to the left. + * + * See ucx_buffer_shift() for details. Note, however, that this method expects + * a positive shift offset. + * + * Since a left shift cannot fail due to memory allocation problems, this + * function always returns zero. + * + * @param buffer the buffer + * @param shift the shift offset + * @return always zero + * @see ucx_buffer_shift() + */ +int ucx_buffer_shift_left(UcxBuffer* buffer, size_t shift); + + /** * Moves the position of the buffer. * @@ -260,7 +321,7 @@ * @param str the string * @return the number of bytes written */ -size_t ucx_buffer_puts(UcxBuffer *buffer, char *str); +size_t ucx_buffer_puts(UcxBuffer *buffer, const char *str); /** * Returns the complete buffer content as sstr_t.
--- a/test/buffer_tests.c Wed May 09 15:04:15 2018 +0200 +++ b/test/buffer_tests.c Wed May 09 20:15:10 2018 +0200 @@ -625,3 +625,114 @@ ucx_buffer_free(b); } + +UCX_TEST(test_ucx_buffer_shl) { + + const char* hw = "Shift the World!"; + + UcxBuffer *b = ucx_buffer_new(NULL, 20, UCX_BUFFER_DEFAULT); + ucx_buffer_puts(b, hw); + b->pos = 5; + + UCX_TEST_BEGIN + char* expected; + + ucx_buffer_shift_left(b, 2); + expected = "ift the World!"; + + UCX_TEST_ASSERT(b->pos == 3, "position after normal shl wrong"); + UCX_TEST_ASSERT(b->size == strlen(expected), "size after normal shl wrong"); + UCX_TEST_ASSERT(!memcmp(b->space, expected, b->size), + "contents after normal shl wrong"); + + + ucx_buffer_shift_left(b, 5); + expected = "he World!"; + + UCX_TEST_ASSERT(b->pos == 0, "position after overshift left wrong"); + UCX_TEST_ASSERT(b->size == strlen(expected), + "size after overshift left wrong"); + UCX_TEST_ASSERT(!memcmp(b->space, expected, b->size), + "contents after overshift left wrong"); + + ucx_buffer_shift_left(b, 10); + UCX_TEST_ASSERT(b->pos == 0, "position after 'shl everything away' wrong"); + UCX_TEST_ASSERT(b->size == 0, "size after 'shl everything away' wrong"); + + UCX_TEST_END + + ucx_buffer_free(b); +} + +UCX_TEST(test_ucx_buffer_shr) { + + const char* hw = "Shift the World!"; + + UcxBuffer *b = ucx_buffer_new(NULL, 20, UCX_BUFFER_DEFAULT); + ucx_buffer_puts(b, hw); + b->pos = 12; + + UCX_TEST_BEGIN + char* expected; + + ucx_buffer_shift_right(b, 2); + expected = "ShShift the World!"; + + UCX_TEST_ASSERT(b->pos == 14, "position after normal shr wrong"); + UCX_TEST_ASSERT(b->size == strlen(expected), "size after normal shr wrong"); + UCX_TEST_ASSERT(!memcmp(b->space, expected, b->size), + "contents after normal shr wrong"); + + + ucx_buffer_shift_right(b, 5); + expected = "ShShiShShift the Wor"; + UCX_TEST_ASSERT(strlen(expected) == b->capacity, + "Test data is wrong, please fix the test."); + + UCX_TEST_ASSERT(b->pos == 19, + "position after overshift right w/o auto-extend wrong"); + UCX_TEST_ASSERT(b->size == 20, + "size after overshift right w/o auto-extend wrong"); + UCX_TEST_ASSERT(b->capacity == 20, + "capacity after overshift right w/o auto-extend wrong"); + UCX_TEST_ASSERT(!memcmp(b->space, expected, b->size), + "contents after overshift right w/o auto-extend wrong"); + + ucx_buffer_shift_right(b, 15); + UCX_TEST_ASSERT(b->pos == b->capacity, "position after 'shr to eof' wrong"); + UCX_TEST_ASSERT(b->size == b->capacity, "size after 'shr to eof' wrong"); + UCX_TEST_ASSERT(ucx_buffer_eof(b), "buffer not eof after 'shr to eof'"); + + UCX_TEST_END + + ucx_buffer_free(b); +} + +UCX_TEST(test_ucx_buffer_shr_ax) { + + const char* hw = "Shift the World!"; + + UcxBuffer *b = ucx_buffer_new(NULL, 20, UCX_BUFFER_AUTOEXTEND); + ucx_buffer_puts(b, hw); + b->pos = 12; + + UCX_TEST_BEGIN + + const char* expected = "Shift the Shift the World!"; + + ucx_buffer_shift_right(b, 10); + UCX_TEST_ASSERT(b->pos == 22, "position after shr w/ auto-extend wrong"); + UCX_TEST_ASSERT(b->size == strlen(expected), + "size after shr w/ auto-extend wrong"); + UCX_TEST_ASSERT(b->capacity >= b->size, + "auto-extension of capacity after shr w/ auto-extend failed"); + UCX_TEST_ASSERT(!ucx_buffer_eof(b), + "buffer should not be eof after shr w/ auto-extend"); + UCX_TEST_ASSERT(!memcmp(b->space, expected, b->size), + "contents wrong after shr w/ auto-extend"); + + UCX_TEST_END + + ucx_buffer_free(b); +} +
--- a/test/buffer_tests.h Wed May 09 15:04:15 2018 +0200 +++ b/test/buffer_tests.h Wed May 09 20:15:10 2018 +0200 @@ -60,6 +60,9 @@ UCX_TEST(test_ucx_buffer_write); UCX_TEST(test_ucx_buffer_write_oob); UCX_TEST(test_ucx_buffer_write_ax); +UCX_TEST(test_ucx_buffer_shl); +UCX_TEST(test_ucx_buffer_shr); +UCX_TEST(test_ucx_buffer_shr_ax); #ifdef __cplusplus
--- a/test/main.c Wed May 09 15:04:15 2018 +0200 +++ b/test/main.c Wed May 09 20:15:10 2018 +0200 @@ -223,6 +223,9 @@ ucx_test_register(suite, test_ucx_buffer_write); ucx_test_register(suite, test_ucx_buffer_write_oob); ucx_test_register(suite, test_ucx_buffer_write_ax); + ucx_test_register(suite, test_ucx_buffer_shl); + ucx_test_register(suite, test_ucx_buffer_shr); + ucx_test_register(suite, test_ucx_buffer_shr_ax); /* Utils Tests*/ ucx_test_register(suite, test_ucx_fprintf);