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