aboutsummaryrefslogtreecommitdiffstats
path: root/src/libutil
diff options
context:
space:
mode:
authorVsevolod Stakhov <vsevolod@highsecure.ru>2016-04-08 14:18:26 +0100
committerVsevolod Stakhov <vsevolod@highsecure.ru>2016-04-08 14:18:26 +0100
commit0bab2739e1f9923bd704d3a70e99c2bda496f87d (patch)
treee9945721600457c72c8e40364f7e27b936406784 /src/libutil
parenta4f8e31bff5546c25aee8bbddb5093c25afa762f (diff)
downloadrspamd-0bab2739e1f9923bd704d3a70e99c2bda496f87d.tar.gz
rspamd-0bab2739e1f9923bd704d3a70e99c2bda496f87d.zip
[Feature] Add preliminary implementation of binary heap
Signed-off-by: Vsevolod Stakhov <vsevolod@highsecure.ru>
Diffstat (limited to 'src/libutil')
-rw-r--r--src/libutil/CMakeLists.txt3
-rw-r--r--src/libutil/heap.c156
-rw-r--r--src/libutil/heap.h71
3 files changed, 229 insertions, 1 deletions
diff --git a/src/libutil/CMakeLists.txt b/src/libutil/CMakeLists.txt
index 0e340dcb8..de59b5fea 100644
--- a/src/libutil/CMakeLists.txt
+++ b/src/libutil/CMakeLists.txt
@@ -18,6 +18,7 @@ SET(LIBRSPAMDUTILSRC
${CMAKE_CURRENT_SOURCE_DIR}/sqlite_utils.c
${CMAKE_CURRENT_SOURCE_DIR}/str_util.c
${CMAKE_CURRENT_SOURCE_DIR}/upstream.c
- ${CMAKE_CURRENT_SOURCE_DIR}/util.c)
+ ${CMAKE_CURRENT_SOURCE_DIR}/util.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/heap.c)
# Rspamdutil
SET(RSPAMD_UTIL ${LIBRSPAMDUTILSRC} PARENT_SCOPE) \ No newline at end of file
diff --git a/src/libutil/heap.c b/src/libutil/heap.c
new file mode 100644
index 000000000..354dfedc5
--- /dev/null
+++ b/src/libutil/heap.c
@@ -0,0 +1,156 @@
+/*-
+ * Copyright 2016 Vsevolod Stakhov
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "config.h"
+#include "libutil/heap.h"
+
+struct rspamd_min_heap {
+ GPtrArray *ar;
+};
+
+#define XORSWAP_DISTINCT(a, b) ((a)^=(b),(b)^=(a),(a)^=(b))
+#define heap_swap(h,e1,e2) do { \
+ guintptr a1 = (guintptr)(h)->ar->pdata[(e2)->idx]; \
+ guintptr a2 = (guintptr)(h)->ar->pdata[(e1)->idx]; \
+ XORSWAP_DISTINCT((e1)->idx, (e2)->idx); \
+ XORSWAP_DISTINCT (a1, a2); \
+ (h)->ar->pdata[(e2)->idx] = (gpointer)a1; \
+ (h)->ar->pdata[(e1)->idx] = (gpointer)a2; \
+} while (0)
+
+#define min_elt(e1, e2) ((e1)->pri <= (e2)->pri ? (e1) : (e2))
+
+/*
+ * Swims element added (or changed) to preserve heap's invariant
+ */
+static void
+rspamd_min_heap_swim (struct rspamd_min_heap *heap,
+ struct rspamd_min_heap_elt *elt)
+{
+ struct rspamd_min_heap_elt *parent;
+
+ while (elt->idx > 0) {
+ parent = g_ptr_array_index (heap->ar, elt->idx / 2);
+
+ if (parent->pri > elt->pri) {
+ heap_swap (heap, elt, parent);
+ }
+ else {
+ break;
+ }
+ }
+}
+
+/*
+ * Sinks the element popped (or changed) to preserve heap's invariant
+ */
+static void
+rspamd_min_heap_sink (struct rspamd_min_heap *heap,
+ struct rspamd_min_heap_elt *elt)
+{
+ struct rspamd_min_heap_elt *c1, *c2, *m;
+
+ while (elt->idx < heap->ar->len - 1) {
+ c1 = g_ptr_array_index (heap->ar, elt->idx * 2);
+ c2 = g_ptr_array_index (heap->ar, elt->idx * 2 + 1);
+ m = min_elt (c1, c2);
+
+ if (elt->pri > m->pri) {
+ heap_swap (heap, elt, m);
+ }
+ else {
+ break;
+ }
+ }
+}
+
+struct rspamd_min_heap *
+rspamd_min_heap_create (gsize reserved_size)
+{
+ struct rspamd_min_heap *heap;
+
+ heap = g_slice_alloc (sizeof (*heap));
+ heap->ar = g_ptr_array_sized_new (reserved_size);
+
+ return heap;
+}
+
+void
+rspamd_min_heap_push (struct rspamd_min_heap *heap,
+ struct rspamd_min_heap_elt *elt)
+{
+ g_assert (heap != NULL);
+ g_assert (elt != NULL);
+
+ /* Add to the end */
+ elt->idx = heap->ar->len;
+ g_ptr_array_add (heap->ar, elt);
+ /* Now swim it up */
+ rspamd_min_heap_swim (heap, elt);
+}
+
+struct rspamd_min_heap_elt*
+rspamd_min_heap_pop (struct rspamd_min_heap *heap)
+{
+ struct rspamd_min_heap_elt *elt, *last;
+
+ g_assert (heap != NULL);
+
+ if (heap->ar->len == 0) {
+ return NULL;
+ }
+
+ elt = g_ptr_array_index (heap->ar, 0);
+ last = g_ptr_array_index (heap->ar, heap->ar->len - 1);
+ /* Now replace elt with the last element and sink it if needed */
+ heap_swap (heap, elt, last);
+ g_ptr_array_remove_index_fast (heap->ar, heap->ar->len - 1);
+ rspamd_min_heap_sink (heap, last);
+
+ return elt;
+}
+
+void
+rspamd_min_heap_update_elt (struct rspamd_min_heap *heap,
+ struct rspamd_min_heap_elt *elt, guint npri)
+{
+ guint oldpri;
+
+ g_assert (heap != NULL);
+ g_assert (elt->idx >= 0 && elt->idx < heap->ar->len);
+
+
+ oldpri = elt->pri;
+ elt->pri = npri;
+
+ if (oldpri > npri) {
+ /* We might need to sink */
+ rspamd_min_heap_sink (heap, elt);
+ }
+ else if (oldpri < npri) {
+ /* We might need to swim */
+ rspamd_min_heap_swim (heap, elt);
+ }
+}
+
+void
+rspamd_min_heap_destroy (struct rspamd_min_heap *heap)
+{
+ if (heap) {
+ g_ptr_array_free (heap->ar, TRUE);
+ g_slice_free1 (sizeof (*heap), heap);
+ }
+}
diff --git a/src/libutil/heap.h b/src/libutil/heap.h
new file mode 100644
index 000000000..58a9a0c88
--- /dev/null
+++ b/src/libutil/heap.h
@@ -0,0 +1,71 @@
+/*-
+ * Copyright 2016 Vsevolod Stakhov
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef SRC_LIBUTIL_HEAP_H_
+#define SRC_LIBUTIL_HEAP_H_
+
+#include "config.h"
+
+/**
+ * Binary minimal heap interface based on glib
+ */
+
+struct rspamd_min_heap_elt {
+ gpointer data;
+ guint pri;
+ guint idx;
+};
+
+struct rspamd_min_heap;
+
+/**
+ * Creates min heap with the specified reserved size and compare function
+ * @param reserved_size reserved size in elements
+ * @return opaque minimal heap
+ */
+struct rspamd_min_heap *rspamd_min_heap_create (gsize reserved_size);
+
+/**
+ * Pushes an element to the heap. `pri` should be initialized to use this function,
+ * `idx` is used internally by heap interface
+ * @param heap heap structure
+ * @param elt element to push
+ */
+void rspamd_min_heap_push (struct rspamd_min_heap *heap,
+ struct rspamd_min_heap_elt *elt);
+
+/**
+ * Pops the minimum element from the heap and reorder the queue
+ * @param heap heap structure
+ * @return minimum element
+ */
+struct rspamd_min_heap_elt* rspamd_min_heap_pop (struct rspamd_min_heap *heap);
+
+/**
+ * Updates priority for the element. It must be in queue (so `idx` should be sane)
+ * @param heap heap structure
+ * @param elt element to update
+ * @param npri new priority
+ */
+void rspamd_min_heap_update_elt (struct rspamd_min_heap *heap,
+ struct rspamd_min_heap_elt *elt, guint npri);
+
+/**
+ * Destroys heap (elements are not destroyed themselves)
+ * @param heap
+ */
+void rspamd_min_heap_destroy (struct rspamd_min_heap *heap);
+
+#endif /* SRC_LIBUTIL_HEAP_H_ */