log-throttle.c \
md4.c \
md5.c \
+ memarea.c \
mempool.c \
mempool-alloconly.c \
mempool-datastack.c \
md4.h \
md5.h \
malloc-overflow.h \
+ memarea.h \
mempool.h \
mkdir-parents.h \
mmap-util.h \
test-llist.c \
test-log-throttle.c \
test-malloc-overflow.c \
+ test-memarea.c \
test-mempool.c \
test-mempool-alloconly.c \
test-pkcs5.c \
--- /dev/null
+/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "memarea.h"
+
+struct memarea {
+ const void *data;
+ size_t size;
+
+ memarea_free_callback_t *callback;
+ void *context;
+
+ int refcount;
+};
+
+static struct memarea memarea_empty = {
+ .refcount = 1,
+};
+
+#undef memarea_init
+struct memarea *
+memarea_init(const void *data, size_t size,
+ memarea_free_callback_t *callback, void *context)
+{
+ struct memarea *area;
+
+ i_assert(callback != NULL);
+
+ area = i_new(struct memarea, 1);
+ area->data = data;
+ area->size = size;
+ area->callback = callback;
+ area->context = context;
+ area->refcount = 1;
+ return area;
+}
+
+struct memarea *memarea_init_empty(void)
+{
+ i_assert(memarea_empty.refcount > 0);
+ memarea_empty.refcount++;
+ return &memarea_empty;
+}
+
+void memarea_ref(struct memarea *area)
+{
+ i_assert(area->refcount > 0);
+ area->refcount++;
+}
+
+void memarea_unref(struct memarea **_area)
+{
+ struct memarea *area = *_area;
+
+ *_area = NULL;
+ i_assert(area->refcount > 0);
+
+ if (--area->refcount > 0)
+ return;
+ i_assert(area != &memarea_empty);
+ area->callback(area->context);
+ i_free(area);
+}
+
+void memarea_free_without_callback(struct memarea **_area)
+{
+ struct memarea *area = *_area;
+
+ *_area = NULL;
+ i_assert(memarea_get_refcount(area) == 1);
+ i_free(area);
+}
+
+unsigned int memarea_get_refcount(struct memarea *area)
+{
+ i_assert(area->refcount > 0);
+ return area->refcount;
+}
+
+const void *memarea_get(struct memarea *area, size_t *size_r)
+{
+ *size_r = area->size;
+ return area->data;
+}
+
+size_t memarea_get_size(struct memarea *area)
+{
+ return area->size;
+}
+
+void memarea_free_callback_noop(void *context ATTR_UNUSED)
+{
+}
--- /dev/null
+#ifndef MEMAREA_H
+#define MEMAREA_H
+
+typedef void memarea_free_callback_t(void *context);
+
+/* Create reference counted memory area. The callback is called when the
+ refcount drops to 0. */
+struct memarea *
+memarea_init(const void *data, size_t size,
+ memarea_free_callback_t *callback, void *context);
+#define memarea_init(data, size, callback, context) \
+ memarea_init(data, size + \
+ CALLBACK_TYPECHECK(callback, void (*)(typeof(context))), \
+ (memarea_free_callback_t *)callback, context)
+/* Returns an empty memory area. */
+struct memarea *memarea_init_empty(void);
+
+void memarea_ref(struct memarea *area);
+void memarea_unref(struct memarea **area);
+/* Free the memory area without calling the callback.
+ This is allowed only when refcount==1. */
+void memarea_free_without_callback(struct memarea **area);
+
+unsigned int memarea_get_refcount(struct memarea *area);
+const void *memarea_get(struct memarea *area, size_t *size_r);
+size_t memarea_get_size(struct memarea *area);
+
+/* free-callback that does nothing */
+void memarea_free_callback_noop(void *context);
+
+#endif
TEST(test_log_throttle)
TEST(test_malloc_overflow)
FATAL(fatal_malloc_overflow)
+TEST(test_memarea)
TEST(test_mempool)
FATAL(fatal_mempool)
TEST(test_mempool_alloconly)
--- /dev/null
+/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */
+
+#include "test-lib.h"
+#include "buffer.h"
+#include "memarea.h"
+
+static bool test_callback_called = FALSE;
+
+static void test_callback(buffer_t *buf)
+{
+ test_assert(!test_callback_called);
+ test_callback_called = TRUE;
+ buffer_free(&buf);
+}
+
+void test_memarea(void)
+{
+ struct memarea *area, *area2;
+ buffer_t *buf;
+ size_t size;
+
+ test_begin("memarea");
+ buf = buffer_create_dynamic(default_pool, 128);
+ buffer_append(buf, "123", 3);
+
+ area = memarea_init(buf->data, buf->used, test_callback, buf);
+ test_assert(memarea_get_refcount(area) == 1);
+ test_assert(memarea_get(area, &size) == buf->data && size == buf->used);
+
+ area2 = area;
+ memarea_ref(area2);
+ test_assert(memarea_get_refcount(area2) == 2);
+ test_assert(memarea_get(area2, &size) == buf->data && size == buf->used);
+ memarea_unref(&area2);
+ test_assert(area2 == NULL);
+ test_assert(!test_callback_called);
+
+ memarea_unref(&area);
+ test_assert(test_callback_called);
+
+ test_end();
+}