changeset 798:29db876d8157

buffer: add buffer_init_heap to support stack allocated heap buffers The underlying buffer is still heap allocated. Sometimes, it is wasteful to allocate the buffer structure itself on the heap only to free it shortly thereafter. In those cases, we can use the stack to hold the struct buffer (much like we do for the sink/static/etc. buffers) while using the heap for the underlying buffer. Signed-off-by: Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
author Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
date Sat, 28 Mar 2020 11:52:07 -0400
parents 4c0939533a78
children 6371fb111e27
files buffer.c include/jeffpc/buffer.h mapfile-vers tests/test_buffer.c
diffstat 4 files changed, 71 insertions(+), 35 deletions(-) [+]
line wrap: on
line diff
--- a/buffer.c	Sat Mar 28 11:51:48 2020 -0400
+++ b/buffer.c	Sat Mar 28 11:52:07 2020 -0400
@@ -29,21 +29,18 @@
 struct buffer *buffer_alloc(size_t expected_size)
 {
 	struct buffer *buffer;
+	int ret;
 
 	buffer = malloc(sizeof(struct buffer));
 	if (!buffer)
 		return ERR_PTR(-ENOMEM);
 
-	buffer->data = malloc(expected_size ? expected_size : 1);
-	if (!buffer->data) {
+	ret = buffer_init_heap(buffer, expected_size);
+	if (ret) {
 		free(buffer);
-		return ERR_PTR(-ENOMEM);
+		return ERR_PTR(ret);
 	}
 
-	buffer->off = 0;
-	buffer->size = 0;
-	buffer->allocsize = expected_size;
-	buffer->ops = &heap_buffer;
 	buffer->heap = true;
 
 	return buffer;
@@ -61,6 +58,21 @@
 		free(buffer);
 }
 
+int buffer_init_heap(struct buffer *buffer, size_t expected_size)
+{
+	buffer->data = malloc(expected_size ? expected_size : 1);
+	if (!buffer->data)
+		return -ENOMEM;
+
+	buffer->off = 0;
+	buffer->size = 0;
+	buffer->allocsize = expected_size;
+	buffer->ops = &heap_buffer;
+	buffer->heap = false;
+
+	return 0;
+}
+
 void buffer_init_sink(struct buffer *buffer)
 {
 	buffer->data = NULL;
--- a/include/jeffpc/buffer.h	Sat Mar 28 11:51:48 2020 -0400
+++ b/include/jeffpc/buffer.h	Sat Mar 28 11:52:07 2020 -0400
@@ -62,6 +62,9 @@
 extern struct buffer *buffer_alloc(size_t expected_size);
 extern void buffer_free(struct buffer *buffer);
 
+/* a buffer that uses a growable heap buffer */
+extern int buffer_init_heap(struct buffer *buffer, size_t expected_size);
+
 /* a buffer that behaves similarly to /dev/null - all appends are lost */
 extern void buffer_init_sink(struct buffer *buffer);
 /*
--- a/mapfile-vers	Sat Mar 28 11:51:48 2020 -0400
+++ b/mapfile-vers	Sat Mar 28 11:52:07 2020 -0400
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2016-2019 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
+# Copyright (c) 2016-2020 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
@@ -45,6 +45,7 @@
 		buffer_alloc;
 		buffer_append;
 		buffer_free;
+		buffer_init_heap;
 		buffer_init_sink;
 		buffer_init_static;
 		buffer_init_stdio;
--- a/tests/test_buffer.c	Sat Mar 28 11:51:48 2020 -0400
+++ b/tests/test_buffer.c	Sat Mar 28 11:52:07 2020 -0400
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017-2019 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
+ * Copyright (c) 2017-2020 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
@@ -26,6 +26,27 @@
 
 #include "test.c"
 
+/* allocate a heap buffer - either on the heap or on the stack */
+static inline struct buffer *alloc_heap_buffer(struct buffer *buf, size_t size)
+{
+	if (buf) {
+		int ret;
+
+		ret = buffer_init_heap(buf, size);
+		if (ret)
+			fail("buffer_init_heap(%zu) failed: %s", size,
+			     xstrerror(ret));
+	} else {
+		buf = buffer_alloc(size);
+		if (IS_ERR(buf))
+			fail("buffer_alloc(%zu) failed: %s", size,
+			     xstrerror(PTR_ERR(buf)));
+
+	}
+
+	return buf;
+}
+
 static inline void check_data_zeroes(struct buffer *buffer, size_t startoff)
 {
 	const uint8_t *data;
@@ -144,18 +165,15 @@
 	check_truncate_err(buffer, newsize, 0);
 }
 
-static void test_alloc_free(void)
+static void test_alloc_free(struct buffer *_buffer)
 {
 	struct buffer *buffer;
 	size_t i;
 
 	for (i = 0; i < 10; i++) {
-		fprintf(stderr, "%s: iter = %zu...", __func__, i);
+		fprintf(stderr, "%s(%p): iter = %zu...", __func__, _buffer, i);
 
-		buffer = buffer_alloc(i);
-		if (IS_ERR(buffer))
-			fail("buffer_alloc(%zu) failed: %s", i,
-			     xstrerror(PTR_ERR(buffer)));
+		buffer = alloc_heap_buffer(_buffer, i);
 
 		check_data(buffer);
 		check_used(buffer, 0);
@@ -193,7 +211,7 @@
 	check_used(buffer, i);
 }
 
-static void test_append(void)
+static void test_append(struct buffer *_buffer)
 {
 	struct buffer *buffer;
 	size_t startsize;
@@ -201,12 +219,10 @@
 	for (startsize = 0; startsize < 300; startsize++) {
 		uint8_t data[256];
 
-		fprintf(stderr, "%s: iter = %3zu...", __func__, startsize);
+		fprintf(stderr, "%s(%p): iter = %3zu...", __func__, _buffer,
+			startsize);
 
-		buffer = buffer_alloc(startsize);
-		if (IS_ERR(buffer))
-			fail("buffer_alloc(%zu) failed: %s", startsize,
-			     xstrerror(PTR_ERR(buffer)));
+		buffer = alloc_heap_buffer(_buffer, startsize);
 
 		inner_loop(256, buffer, data, check_data);
 
@@ -221,15 +237,12 @@
 	}
 }
 
-static void test_truncate_grow(void)
+static void test_truncate_grow(struct buffer *_buffer)
 {
 	struct buffer *buffer;
 	size_t i;
 
-	buffer = buffer_alloc(0);
-	if (IS_ERR(buffer))
-		fail("buffer_alloc(%zu) failed: %s", 0,
-		     xstrerror(PTR_ERR(buffer)));
+	buffer = alloc_heap_buffer(_buffer, 0);
 
 	for (i = 0; i < 50000; i += 13) {
 		fprintf(stderr, "%s: iter = %3zu...", __func__, i);
@@ -244,16 +257,13 @@
 	buffer_free(buffer);
 }
 
-static void test_truncate_shrink(void)
+static void test_truncate_shrink(struct buffer *_buffer)
 {
 	const size_t maxsize = 5000 * sizeof(uint64_t);
 	struct buffer *buffer;
 	size_t i;
 
-	buffer = buffer_alloc(maxsize);
-	if (IS_ERR(buffer))
-		fail("buffer_alloc(%zu) failed: %s", maxsize,
-		     xstrerror(PTR_ERR(buffer)));
+	buffer = alloc_heap_buffer(_buffer, maxsize);
 
 	/* append some random data */
 	for (i = 0; i < maxsize; i += sizeof(uint64_t)) {
@@ -395,10 +405,20 @@
 
 void test(void)
 {
-	test_alloc_free();
-	test_append();
-	test_truncate_grow();
-	test_truncate_shrink();
+	struct buffer stack_buf;
+
+	/* stack allocated buffer struct */
+	test_alloc_free(&stack_buf);
+	test_append(&stack_buf);
+	test_truncate_grow(&stack_buf);
+	test_truncate_shrink(&stack_buf);
+
+	/* heap allocated buffer struct */
+	test_alloc_free(NULL);
+	test_append(NULL);
+	test_truncate_grow(NULL);
+	test_truncate_shrink(NULL);
+
 	test_sink();
 	test_static_const_arg();
 	test_static_ro();