/*- * 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 "rspamd.h" #include "heap.h" #include "ottery.h" static const guint niter = 100500; static const guint nrem = 100; static inline struct rspamd_min_heap_elt * new_elt (guint pri) { struct rspamd_min_heap_elt *elt; elt = g_slice_alloc0 (sizeof (*elt)); elt->pri = pri; return elt; } static gdouble heap_nelts_test (guint nelts) { struct rspamd_min_heap *heap; struct rspamd_min_heap_elt *elts; gdouble t1, t2; guint i; heap = rspamd_min_heap_create (nelts); /* Preallocate all elts */ elts = g_slice_alloc (sizeof (*elts) * nelts); for (i = 0; i < nelts; i ++) { elts[i].pri = ottery_rand_uint32 () % G_MAXINT32 + 1; elts[i].idx = 0; } t1 = rspamd_get_virtual_ticks (); for (i = 0; i < nelts; i ++) { rspamd_min_heap_push (heap, &elts[i]); } for (i = 0; i < nelts; i ++) { (void)rspamd_min_heap_pop (heap); } t2 = rspamd_get_virtual_ticks (); g_slice_free1 (sizeof (*elts) * nelts, elts); rspamd_min_heap_destroy (heap); return (t2 - t1); } void rspamd_heap_test_func (void) { struct rspamd_min_heap *heap; struct rspamd_min_heap_elt *elt, *telt; guint i; guint prev; gdouble t[16]; /* Push + update */ heap = rspamd_min_heap_create (32); elt = new_elt (2); elt->data = GINT_TO_POINTER (1); rspamd_min_heap_push (heap, elt); elt = new_elt (3); elt->data = GINT_TO_POINTER (2); rspamd_min_heap_push (heap, elt); elt = new_elt (4); elt->data = GINT_TO_POINTER (3); rspamd_min_heap_push (heap, elt); rspamd_min_heap_update_elt (heap, elt, 0); elt = rspamd_min_heap_pop (heap); g_assert (elt->data == GINT_TO_POINTER (3)); rspamd_min_heap_destroy (heap); /* Push + remove */ heap = rspamd_min_heap_create (32); elt = new_elt (2); elt->data = GINT_TO_POINTER (1); rspamd_min_heap_push (heap, elt); rspamd_min_heap_remove_elt (heap, elt); elt = new_elt (3); elt->data = GINT_TO_POINTER (2); rspamd_min_heap_push (heap, elt); elt = rspamd_min_heap_pop (heap); g_assert (elt->data == GINT_TO_POINTER (2)); elt = rspamd_min_heap_pop (heap); g_assert (elt == NULL); /* Push + push + remove + pop */ elt = new_elt (2); elt->data = GINT_TO_POINTER (1); rspamd_min_heap_push (heap, elt); telt = elt; elt = new_elt (3); elt->data = GINT_TO_POINTER (2); rspamd_min_heap_push (heap, elt); rspamd_min_heap_remove_elt (heap, telt); elt = rspamd_min_heap_pop (heap); g_assert (elt->data == GINT_TO_POINTER (2)); rspamd_min_heap_destroy (heap); /* Bulk test */ heap = rspamd_min_heap_create (32); for (i = 100; i > 0; i --) { elt = new_elt (i - 1); rspamd_min_heap_push (heap, elt); } for (i = 0; i < 100; i ++) { elt = rspamd_min_heap_pop (heap); g_assert (elt->pri == i); } rspamd_min_heap_destroy (heap); /* Fuzz test */ heap = rspamd_min_heap_create (128); /* Add */ for (i = 0; i < niter; i ++) { elt = new_elt (ottery_rand_uint32 () % G_MAXINT32 + 1); rspamd_min_heap_push (heap, elt); } /* Remove */ for (i = 0; i < nrem; i ++) { elt = rspamd_min_heap_index (heap, ottery_rand_uint32 () % niter); rspamd_min_heap_remove_elt (heap, elt); } /* Update */ for (i = 0; i < niter / 10; i ++) { elt = rspamd_min_heap_index (heap, ottery_rand_uint32 () % (niter - nrem)); rspamd_min_heap_update_elt (heap, elt, ottery_rand_uint32 () % G_MAXINT32 + 1); } prev = 0; /* Pop and check invariant */ for (i = 0; i < niter - nrem; i ++) { elt = rspamd_min_heap_pop (heap); if (prev != 0) { g_assert (elt->pri >= prev); } prev = elt->pri; } rspamd_min_heap_destroy (heap); /* Complexity test (should be O(n * logn) */ for (i = 1; i <= G_N_ELEMENTS (t); i ++) { t[i - 1] = heap_nelts_test (0x1 << (i + 4)); } for (i = 1; i <= G_N_ELEMENTS (t); i ++) { rspamd_printf ("Elements: %d, time: %.4f\n", 0x1 << (i + 4), t[i - 1]); } }