changeset 256:f82b45b662c9

buffer: use an ops vector to customize buffer behavior The bool flags seemed like a good idea, but it turns out that an ops vector is much easier to understand and reason about. Signed-off-by: Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
author Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
date Tue, 11 Jul 2017 00:17:36 +0300
parents 2da89ff8a6c7
children 068955620d10
files CMakeLists.txt buffer.c buffer_const.c buffer_heap.c buffer_impl.h buffer_sink.c include/jeffpc/buffer.h test_buffer.c
diffstat 8 files changed, 208 insertions(+), 29 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Tue Jul 11 00:43:25 2017 +0300
+++ b/CMakeLists.txt	Tue Jul 11 00:17:36 2017 +0300
@@ -83,6 +83,9 @@
 add_library(jeffpc SHARED
 	array.c
 	buffer.c
+	buffer_const.c
+	buffer_heap.c
+	buffer_sink.c
 	cstr.c
 	error.c
 	hexdump.c
--- a/buffer.c	Tue Jul 11 00:43:25 2017 +0300
+++ b/buffer.c	Tue Jul 11 00:17:36 2017 +0300
@@ -22,7 +22,7 @@
 
 #include <stdbool.h>
 
-#include <jeffpc/buffer.h>
+#include "buffer_impl.h"
 
 struct buffer *buffer_alloc(size_t expected_size)
 {
@@ -40,8 +40,7 @@
 
 	buffer->used = 0;
 	buffer->allocsize = expected_size;
-	buffer->sink = false;
-	buffer->heap = true;
+	buffer->ops = &heap_buffer;
 
 	return buffer;
 }
@@ -51,7 +50,9 @@
 	if (!buffer)
 		return;
 
-	free(buffer->data);
+	if (buffer->ops->free)
+		buffer->ops->free(buffer->data);
+
 	free(buffer);
 }
 
@@ -60,9 +61,7 @@
 	buffer->data = NULL;
 	buffer->used = 0;
 	buffer->allocsize = SIZE_MAX;
-	buffer->sink = true;
-	buffer->heap = false;
-
+	buffer->ops = &sink_buffer;
 }
 
 void buffer_init_const(struct buffer *buffer, const void *data, size_t size)
@@ -70,25 +69,25 @@
 	buffer->data = (void *) data;
 	buffer->used = size;
 	buffer->allocsize = size;
-	buffer->sink = false;
-	buffer->heap = false;
+	buffer->ops = &const_buffer;
 }
 
 static int resize(struct buffer *buffer, size_t newsize)
 {
 	void *tmp;
 
-	ASSERT(!buffer->sink);
-	ASSERT(buffer->heap);
-
 	if (newsize <= buffer->allocsize)
 		return 0;
 
-	tmp = realloc(buffer->data, newsize);
+	if (!buffer->ops->realloc)
+		return -ENOTSUP;
+
+	tmp = buffer->ops->realloc(buffer->data, newsize);
 	if (!tmp)
 		return -ENOMEM;
 
 	buffer->data = tmp;
+	buffer->allocsize = newsize;
 
 	return 0;
 }
@@ -97,24 +96,50 @@
 {
 	int ret;
 
-	if (!buffer || (!buffer->sink && !buffer->heap))
+	if (!buffer)
+		return -EINVAL;
+
+	if ((data && !size) || (!data && size))
 		return -EINVAL;
 
+	if (buffer->ops->check_append) {
+		ret = buffer->ops->check_append(buffer, data, size);
+		if (ret)
+			return ret;
+	}
+
 	if (!data && !size)
 		return 0; /* append(..., NULL, 0) is a no-op */
 
-	if (!data || !size)
-		return -EINVAL;
+	ret = resize(buffer, buffer->used + size);
+	if (ret)
+		return ret;
 
-	if (!buffer->sink) {
-		ret = resize(buffer, buffer->used + size);
-		if (ret)
-			return ret;
-
-		memcpy(buffer->data + buffer->used, data, size);
-	}
+	buffer->ops->copyin(buffer, buffer->used, data, size);
 
 	buffer->used += size;
 
 	return 0;
 }
+
+/*
+ * Generic implementations
+ */
+
+/* copyin implementations */
+void generic_buffer_copyin_memcpy(struct buffer *buffer, size_t off,
+				  const void *newdata, size_t newdatalen)
+{
+	memcpy(buffer->data + off, newdata, newdatalen);
+}
+
+void generic_buffer_copyin_nop(struct buffer *buffer, size_t off,
+			       const void *newdata, size_t newdatalen)
+{
+}
+
+void generic_buffer_copyin_panic(struct buffer *buffer, size_t off,
+				 const void *newdata, size_t newdatalen)
+{
+	panic("buffer copyin called");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/buffer_const.c	Tue Jul 11 00:17:36 2017 +0300
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2017 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
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "buffer_impl.h"
+
+static int const_buffer_check_append(struct buffer *buffer, const void *data,
+				     size_t size)
+{
+	return -EROFS;
+}
+
+const struct buffer_ops const_buffer = {
+	.check_append = const_buffer_check_append,
+
+	/*
+	 * no need for:
+	 *  - realloc since we have a borrowed const buffer
+	 *  - free since we have a borrowed const buffer
+	 */
+
+	.copyin = generic_buffer_copyin_panic,
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/buffer_heap.c	Tue Jul 11 00:17:36 2017 +0300
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2017 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
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "buffer_impl.h"
+
+const struct buffer_ops heap_buffer = {
+	.realloc = realloc,
+	.free = free,
+	.copyin = generic_buffer_copyin_memcpy,
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/buffer_impl.h	Tue Jul 11 00:17:36 2017 +0300
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2017 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
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __JEFFPC_BUFFER_IMPL_H
+#define __JEFFPC_BUFFER_IMPL_H
+
+#include <jeffpc/buffer.h>
+
+extern const struct buffer_ops heap_buffer;
+extern const struct buffer_ops sink_buffer;
+extern const struct buffer_ops const_buffer;
+
+/* copyin implementations */
+extern void generic_buffer_copyin_memcpy(struct buffer *buffer, size_t off,
+					 const void *newdata, size_t newdatalen);
+extern void generic_buffer_copyin_nop(struct buffer *buffer, size_t off,
+				      const void *newdata, size_t newdatalen);
+extern void generic_buffer_copyin_panic(struct buffer *buffer, size_t off,
+					const void *newdata, size_t newdatalen);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/buffer_sink.c	Tue Jul 11 00:17:36 2017 +0300
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2017 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
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "buffer_impl.h"
+
+const struct buffer_ops sink_buffer = {
+	/*
+	 * no need for:
+	 *  - realloc since we use SIZE_MAX alloc size
+	 *  - free since there is nothing to free
+	 */
+
+	.copyin = generic_buffer_copyin_nop,
+};
--- a/include/jeffpc/buffer.h	Tue Jul 11 00:43:25 2017 +0300
+++ b/include/jeffpc/buffer.h	Tue Jul 11 00:17:36 2017 +0300
@@ -28,12 +28,23 @@
 
 #include <jeffpc/error.h>
 
+struct buffer;
+
+struct buffer_ops {
+	/* op checking */
+	int (*check_append)(struct buffer *, const void *, size_t);
+
+	/* data manipulation */
+	void *(*realloc)(void *, size_t);
+	void (*free)(void *);
+	void (*copyin)(struct buffer *, size_t, const void *, size_t);
+};
+
 struct buffer {
 	void *data;		/* the data itself */
 	size_t used;		/* bytes filled */
 	size_t allocsize;	/* allocated buffer size */
-	bool sink:1;		/* no actual data is stored */
-	bool heap:1;		/* the void data is borrowed */
+	const struct buffer_ops *ops;
 };
 
 extern struct buffer *buffer_alloc(size_t expected_size);
@@ -55,9 +66,6 @@
 
 static inline const void *buffer_data(struct buffer *buffer)
 {
-	if (buffer->sink)
-		return NULL;
-
 	return buffer->data;
 }
 
--- a/test_buffer.c	Tue Jul 11 00:43:25 2017 +0300
+++ b/test_buffer.c	Tue Jul 11 00:17:36 2017 +0300
@@ -212,7 +212,7 @@
 	for (i = 0; i < 10; i++) {
 		fprintf(stderr, "%s: iter = %d...", __func__, i);
 
-		check_append_err(&buffer, "abc", 3, -EINVAL);
+		check_append_err(&buffer, "abc", 3, -EROFS);
 		check_used(&buffer, strlen(rawdata));
 		check_data_ptr(&buffer, rawdata);