Mercurial > libjeffpc
changeset 849:1c4d7ff0a682
buffer: add buffer_insert & buffer_insert_c
Like appending, but at any offset - not just at the end of existing data.
Signed-off-by: Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
author | Josef 'Jeff' Sipek <jeffpc@josefsipek.net> |
---|---|
date | Thu, 16 Sep 2021 22:39:12 -0400 |
parents | 8371ff905869 |
children | 3a53836f2274 |
files | buffer.c buffer_heap.c buffer_impl.h buffer_sink.c buffer_static.c include/jeffpc/buffer.h mapfile-vers tests/test_buffer.c |
diffstat | 8 files changed, 277 insertions(+), 25 deletions(-) [+] |
line wrap: on
line diff
--- a/buffer.c Wed Oct 13 19:35:23 2021 -0400 +++ b/buffer.c Thu Sep 16 22:39:12 2021 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Josef 'Jeff' Sipek <jeffpc@josefsipek.net> + * Copyright (c) 2017-2021 Josef 'Jeff' Sipek <jeffpc@josefsipek.net> * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -116,7 +116,8 @@ return 0; } -int buffer_append(struct buffer *buffer, const void *data, size_t size) +int buffer_insert(struct buffer *buffer, const void *data, size_t size, + size_t off) { int ret; @@ -126,20 +127,26 @@ if (!data && size) return -EINVAL; - if (buffer->ops->check_append) { - ret = buffer->ops->check_append(buffer, data, size); + if (buffer->ops->check_insert) { + ret = buffer->ops->check_insert(buffer, data, size, off); if (ret) return ret; } + if (off > buffer->size) + return -EINVAL; /* can't insert past EOF */ + if (!size) - return 0; /* append(..., 0) is a no-op */ + return 0; /* insert(..., 0, ...) is a no-op */ ret = resize(buffer, buffer->size + size); if (ret) return ret; - buffer->ops->copyin(buffer, buffer->size, data, size); + if (off < buffer->size) + buffer->ops->move(buffer, off + size, off, buffer->size - off); + + buffer->ops->copyin(buffer, off, data, size); buffer->size += size; @@ -363,3 +370,22 @@ { panic("buffer copyout called"); } + +/* move implementations */ +void generic_buffer_move_memmove(struct buffer *buffer, size_t dst, + size_t src, size_t len) +{ + memmove(buffer->data + dst, buffer->data + src, len); +} + +void generic_buffer_move_nop(struct buffer *buffer, size_t dst, + size_t src, size_t len) +{ +} + +void generic_buffer_move_panic(struct buffer *buffer, size_t dst, + size_t src, size_t len) +{ + panic("buffer move called"); +} +
--- a/buffer_heap.c Wed Oct 13 19:35:23 2021 -0400 +++ b/buffer_heap.c Thu Sep 16 22:39:12 2021 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2018 Josef 'Jeff' Sipek <jeffpc@josefsipek.net> + * Copyright (c) 2017-2021 Josef 'Jeff' Sipek <jeffpc@josefsipek.net> * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,4 +28,5 @@ .clear = generic_buffer_clear_memset, .copyin = generic_buffer_copyin_memcpy, .copyout = generic_buffer_copyout_memcpy, + .move = generic_buffer_move_memmove, };
--- a/buffer_impl.h Wed Oct 13 19:35:23 2021 -0400 +++ b/buffer_impl.h Thu Sep 16 22:39:12 2021 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Josef 'Jeff' Sipek <jeffpc@josefsipek.net> + * Copyright (c) 2017-2021 Josef 'Jeff' Sipek <jeffpc@josefsipek.net> * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -54,4 +54,12 @@ extern void generic_buffer_copyout_panic(struct buffer *buffer, size_t off, void *data, size_t datalen); +/* move implementations */ +extern void generic_buffer_move_memmove(struct buffer *buffer, size_t dst, + size_t src, size_t len); +extern void generic_buffer_move_nop(struct buffer *buffer, size_t dst, + size_t src, size_t len); +extern void generic_buffer_move_panic(struct buffer *buffer, size_t dst, + size_t src, size_t len); + #endif
--- a/buffer_sink.c Wed Oct 13 19:35:23 2021 -0400 +++ b/buffer_sink.c Thu Sep 16 22:39:12 2021 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2018 Josef 'Jeff' Sipek <jeffpc@josefsipek.net> + * Copyright (c) 2017-2021 Josef 'Jeff' Sipek <jeffpc@josefsipek.net> * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -40,4 +40,5 @@ .clear = generic_buffer_clear_nop, .copyin = generic_buffer_copyin_nop, .copyout = generic_buffer_copyout_panic, + .move = generic_buffer_move_nop, };
--- a/buffer_static.c Wed Oct 13 19:35:23 2021 -0400 +++ b/buffer_static.c Thu Sep 16 22:39:12 2021 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Josef 'Jeff' Sipek <jeffpc@josefsipek.net> + * Copyright (c) 2017-2021 Josef 'Jeff' Sipek <jeffpc@josefsipek.net> * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -41,14 +41,14 @@ * buffer. */ -static int static_buffer_check_append_ro(struct buffer *buffer, const void *data, - size_t size) +static int static_buffer_check_insert_ro(struct buffer *buffer, const void *data, + size_t size, size_t off) { return -EROFS; } -static int static_buffer_check_append_rw(struct buffer *buffer, const void *data, - size_t size) +static int static_buffer_check_insert_rw(struct buffer *buffer, const void *data, + size_t size, size_t off) { if ((buffer->size + size) <= buffer->allocsize) return 0; @@ -86,22 +86,24 @@ /* a read-only static buffer */ const struct buffer_ops static_buffer_ro = { - .check_append = static_buffer_check_append_ro, + .check_insert = static_buffer_check_insert_ro, .check_truncate = static_buffer_check_truncate_ro, .check_write = static_buffer_check_write_ro, .clear = generic_buffer_clear_panic, .copyin = generic_buffer_copyin_panic, .copyout = generic_buffer_copyout_memcpy, + .move = generic_buffer_move_panic, }; /* a read-write static buffer */ const struct buffer_ops static_buffer_rw = { - .check_append = static_buffer_check_append_rw, + .check_insert = static_buffer_check_insert_rw, .check_truncate = static_buffer_check_truncate_rw, .check_write = static_buffer_check_write_rw, .clear = generic_buffer_clear_memset, .copyin = generic_buffer_copyin_memcpy, .copyout = generic_buffer_copyout_memcpy, + .move = generic_buffer_move_memmove, };
--- a/include/jeffpc/buffer.h Wed Oct 13 19:35:23 2021 -0400 +++ b/include/jeffpc/buffer.h Thu Sep 16 22:39:12 2021 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Josef 'Jeff' Sipek <jeffpc@josefsipek.net> + * Copyright (c) 2017-2021 Josef 'Jeff' Sipek <jeffpc@josefsipek.net> * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -34,7 +34,7 @@ struct buffer_ops { /* op checking */ - int (*check_append)(struct buffer *, const void *, size_t); + int (*check_insert)(struct buffer *, const void *, size_t, size_t); int (*check_read)(struct buffer *, void *, size_t, size_t); int (*check_truncate)(struct buffer *, size_t); int (*check_write)(struct buffer *, const void *, size_t, size_t); @@ -46,6 +46,7 @@ void (*clear)(struct buffer *, size_t, size_t); void (*copyin)(struct buffer *, size_t, const void *, size_t); void (*copyout)(struct buffer *, size_t, void *, size_t); + void (*move)(struct buffer *, size_t, size_t, size_t); }; struct buffer { @@ -76,7 +77,8 @@ extern void buffer_init_static(struct buffer *buffer, const void *data, size_t size, size_t allocsize, bool writable); -extern int buffer_append(struct buffer *buffer, const void *data, size_t size); +extern int buffer_insert(struct buffer *buffer, const void *data, size_t size, + size_t off); /* append specified number of bytes from src to dst */ extern int buffer_copy(struct buffer *dst, struct buffer *src, size_t len); extern ssize_t buffer_seek(struct buffer *buffer, off_t offset, int whence); @@ -126,6 +128,17 @@ return buffer_data_current_mutable(buffer); } +static inline int buffer_insert_c(struct buffer *buffer, char c, size_t off) +{ + return buffer_insert(buffer, &c, 1, off); +} + +static inline int buffer_append(struct buffer *buffer, const void *data, + size_t size) +{ + return buffer_insert(buffer, data, size, buffer->size); +} + static inline int buffer_append_c(struct buffer *buffer, char c) { return buffer_append(buffer, &c, 1);
--- a/mapfile-vers Wed Oct 13 19:35:23 2021 -0400 +++ b/mapfile-vers Thu Sep 16 22:39:12 2021 -0400 @@ -1,5 +1,5 @@ # -# Copyright (c) 2016-2021 Josef 'Jeff' Sipek <jeffpc@josefsipek.net> +# Copyright (c) 2016-2022 Josef 'Jeff' Sipek <jeffpc@josefsipek.net> # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -55,12 +55,12 @@ # buffer buffer_alloc; - buffer_append; buffer_copy; buffer_free; buffer_init_heap; buffer_init_sink; buffer_init_static; + buffer_insert; buffer_pread; buffer_pwrite; buffer_seek;
--- a/tests/test_buffer.c Wed Oct 13 19:35:23 2021 -0400 +++ b/tests/test_buffer.c Thu Sep 16 22:39:12 2021 -0400 @@ -135,6 +135,35 @@ fail("buffer_size() == %zu, should be %zu", got, expected); } +static inline void check_insert_err(struct buffer *buffer, const void *ptr, + size_t len, size_t off, int expected_ret) +{ + int ret; + + ret = buffer_insert(buffer, ptr, len, off); + if (ret == expected_ret) + return; + + if (ret && expected_ret) + fail("buffer_insert(..., %p, %zu, %zu) failed with wrong error: " + "got %s, expected %s", ptr, len, off, xstrerror(ret), + xstrerror(expected_ret)); + if (ret && !expected_ret) + fail("buffer_insert(..., %p, %zu, %zu) failed but it wasn't " + "supposed to: %s", ptr, len, off, xstrerror(ret)); + if (!ret && expected_ret) + fail("buffer_insert(..., %p, %zu, %zu) succeeded but it wasn't " + "supposed to. Expected error: %s", ptr, len, off, + xstrerror(expected_ret)); + fail("impossible condition occured"); +} + +static inline void check_insert(struct buffer *buffer, const void *ptr, + size_t len, size_t off) +{ + check_insert_err(buffer, ptr, len, off, 0); +} + static inline void check_append_err(struct buffer *buffer, const void *ptr, size_t len, int expected_ret) { @@ -211,6 +240,79 @@ } } +static size_t insert_idx_fwd(size_t niter, size_t i, bool raw) +{ + return i; +} + +static size_t insert_idx_rev(size_t niter, size_t i, bool raw) +{ + return raw ? (niter - i - 1) : 0; +} + +static size_t insert_idx_mid(size_t niter, size_t i, bool raw) +{ + if (raw) + return !i ? 0 : (niter - i); + else + return i ? 1 : 0; +} + +static void do_test_insert(struct buffer *_buffer, const char *func, + const char *name, + size_t (*idx)(size_t, size_t, bool)) +{ + const size_t niter = 256; + struct buffer *buffer; + size_t startsize; + + for (startsize = 0; startsize < 300; startsize++) { + uint8_t data[niter]; + size_t i; + + fprintf(stderr, "%s(%p): %s, iter = %3zu...", func, _buffer, + name, startsize); + + buffer = alloc_heap_buffer(_buffer, startsize); + + for (i = 0; i < niter; i++) { + uint8_t byte = i; + + data[idx(niter, i, true)] = i; + + check_data(buffer); + check_used(buffer, i); + check_insert(buffer, &byte, 1, idx(niter, i, false)); + check_data(buffer); + check_used(buffer, i + 1); + + /* truncate to same size */ + check_truncate(buffer, buffer_size(buffer)); + check_data(buffer); + check_used(buffer, i + 1); + } + + check_data(buffer); + check_used(buffer, i); + + if (memcmp(data, get_buffer_data(buffer), sizeof(data))) + fail("buffered data mismatches expectations"); + + buffer_free(buffer); + + memset(data, 0, sizeof(data)); + + fprintf(stderr, "ok.\n"); + } +} + +static void test_insert(struct buffer *_buffer) +{ + do_test_insert(_buffer, __func__, "forward", insert_idx_fwd); + do_test_insert(_buffer, __func__, "reverse", insert_idx_rev); + do_test_insert(_buffer, __func__, "middle", insert_idx_mid); +} + static void inner_loop_append(size_t niter, struct buffer *buffer, uint8_t *data, void (*check)(struct buffer *)) { @@ -381,8 +483,11 @@ void test_static_ro(void) { const char rawdata[] = COMMON_TEST_STRING; + const size_t insert_offsets[] = { + 0, 1, strlen(rawdata) - 1, strlen(rawdata), strlen(rawdata) + 1, + }; struct buffer buffer; - int i; + size_t i; buffer_init_static(&buffer, rawdata, strlen(rawdata), strlen(rawdata), false); @@ -390,13 +495,20 @@ check_used(&buffer, strlen(rawdata)); check_data_ptr(&buffer, rawdata); - for (i = 0; i < 10; i++) { - fprintf(stderr, "%s: iter = %d...", __func__, i); + for (i = 0; i < ARRAY_LEN(insert_offsets); i++) { + fprintf(stderr, "%s: insert to a buffer, offset=%zu...", + __func__, insert_offsets[i]); - check_append_err(&buffer, "abc", 3, -EROFS); + check_insert_err(&buffer, "abc", 3, insert_offsets[i], -EROFS); check_used(&buffer, strlen(rawdata)); check_data_ptr(&buffer, rawdata); + fprintf(stderr, "ok.\n"); + } + + for (i = 0; i < 10; i++) { + fprintf(stderr, "%s: truncate, iter = %zu...", __func__, i); + check_truncate_err(&buffer, i * strlen(rawdata) / 5, -EROFS); check_used(&buffer, strlen(rawdata)); check_data_ptr(&buffer, rawdata); @@ -409,8 +521,12 @@ { char rawdata[] = COMMON_TEST_STRING; const size_t rawlen = strlen(rawdata); + const size_t insert_offsets[] = { + 0, 1, strlen(rawdata) - 1, strlen(rawdata), strlen(rawdata) + 1, + }; struct buffer buffer; size_t size; + size_t i; for (size = 0; size <= rawlen; size++) { buffer_init_static(&buffer, rawdata, size, rawlen, true); @@ -421,6 +537,17 @@ check_data_ptr(&buffer, rawdata); fprintf(stderr, "ok.\n"); + for (i = 0; i < ARRAY_LEN(insert_offsets); i++) { + fprintf(stderr, "%s(%zu): insert to a buffer without " + "enough space, offset=%zu...", __func__, size, + insert_offsets[i]); + check_insert_err(&buffer, COMMON_TEST_STRING, rawlen + 1, + insert_offsets[i], -ENOSPC); + check_used(&buffer, size); + check_data_ptr(&buffer, rawdata); + fprintf(stderr, "ok.\n"); + } + fprintf(stderr, "%s(%zu): append to a buffer without enough " "space...", __func__, size); check_append_err(&buffer, COMMON_TEST_STRING, rawlen + 1, @@ -437,6 +564,31 @@ fprintf(stderr, "ok.\n"); if (size >= 5) { + for (i = 0; i < ARRAY_LEN(insert_offsets); i++) { + const size_t off = insert_offsets[i]; + + if (off > (size - 5)) + continue; + + check_truncate_err(&buffer, size - 5, 0); + + fprintf(stderr, "%s(%zu): insert too much, " + "offset=%zu...", __func__, size, off); + check_insert_err(&buffer, COMMON_TEST_STRING COMMON_TEST_STRING, + rawlen - size + 11, off, -ENOSPC); + check_used(&buffer, size - 5); + check_data_ptr(&buffer, rawdata); + fprintf(stderr, "ok.\n"); + + fprintf(stderr, "%s(%zu): insert a little, " + "offset=%zu...", __func__, size, off); + check_insert_err(&buffer, COMMON_TEST_STRING, 5, + off, 0); + check_used(&buffer, size); + check_data_ptr(&buffer, rawdata); + fprintf(stderr, "ok.\n"); + } + fprintf(stderr, "%s(%zu): truncate to a smaller than " "initial size...", __func__, size); check_truncate_err(&buffer, size - 5, 0); @@ -460,6 +612,51 @@ } } +static void test_append_insert_equivalence(void) +{ + struct buffer *abuffer; /* for buffer_append */ + struct buffer *ibuffer; /* for buffer_insert */ + uint8_t data[256]; + size_t i; + + fprintf(stderr, "%s: comparing append to insert(end)...", __func__); + + abuffer = alloc_heap_buffer(NULL, 0); + ibuffer = alloc_heap_buffer(NULL, 0); + + for (i = 0; i < sizeof(data); i++) { + uint8_t byte = i; + + data[i] = i; + + check_data(abuffer); + check_data(ibuffer); + check_used(abuffer, i); + check_used(ibuffer, i); + + check_append(abuffer, &byte, 1); + check_insert(ibuffer, &byte, 1, i); + + check_data(abuffer); + check_data(ibuffer); + check_used(abuffer, i + 1); + check_used(ibuffer, i + 1); + } + + if (memcmp(data, get_buffer_data(abuffer), sizeof(data))) + fail("buffered data (append) mismatches expectations"); + + if (memcmp(data, get_buffer_data(ibuffer), sizeof(data))) + fail("buffered data (insert) mismatches expectations"); + + buffer_free(abuffer); + buffer_free(ibuffer); + + memset(data, 0, sizeof(data)); + + fprintf(stderr, "ok.\n"); +} + void test(void) { struct buffer stack_buf; @@ -467,12 +664,14 @@ /* stack allocated buffer struct */ test_alloc_free(&stack_buf); test_append(&stack_buf); + test_insert(&stack_buf); test_truncate_grow(&stack_buf); test_truncate_shrink(&stack_buf); /* heap allocated buffer struct */ test_alloc_free(NULL); test_append(NULL); + test_insert(NULL); test_truncate_grow(NULL); test_truncate_shrink(NULL); @@ -481,4 +680,6 @@ test_static_const_arg(); test_static_ro(); test_static_rw(); + + test_append_insert_equivalence(); }