]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
[Feature] Add preliminary implementation of binary heap
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Fri, 8 Apr 2016 13:18:26 +0000 (14:18 +0100)
committerVsevolod Stakhov <vsevolod@highsecure.ru>
Fri, 8 Apr 2016 13:18:26 +0000 (14:18 +0100)
Signed-off-by: Vsevolod Stakhov <vsevolod@highsecure.ru>
src/libutil/CMakeLists.txt
src/libutil/heap.c [new file with mode: 0644]
src/libutil/heap.h [new file with mode: 0644]

index 0e340dcb82c5d7c268e5a628439c79f3408f7278..de59b5fea5ddfae9a53906eba3127da53cb318ae 100644 (file)
@@ -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 (file)
index 0000000..354dfed
--- /dev/null
@@ -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 (file)
index 0000000..58a9a0c
--- /dev/null
@@ -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_ */