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