2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-22 09:58:01 +00:00
ovs/tests/test-heap.c

490 lines
12 KiB
C
Raw Normal View History

/*
* Copyright (c) 2012, 2014 Nicira, Inc.
*
* 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.
*/
/* A test for for functions and macros declared in heap.h. */
#include <config.h>
#undef NDEBUG
#include "heap.h"
#include <assert.h>
#include <inttypes.h>
#include <limits.h>
#include <stdlib.h>
#include "command-line.h"
#include "ovstest.h"
#include "random.h"
#include "util.h"
/* Sample heap element. */
struct element {
uint32_t full_pri;
struct heap_node heap_node;
};
static struct element *
element_from_heap_node(const struct heap_node *node)
{
return CONTAINER_OF(node, struct element, heap_node);
}
static int
compare_uint32s(const void *a_, const void *b_)
{
const uint32_t *a = a_;
const uint32_t *b = b_;
return *a < *b ? -1 : *a > *b;
}
/* Verifies that 'heap' is internally consistent and contains all 'n' of the
* 'priorities'. */
static void
check_heap(const struct heap *heap, const uint32_t priorities[], size_t n)
{
uint32_t *priorities_copy;
uint32_t *elements_copy;
struct element *element;
size_t i;
assert(heap_count(heap) == n);
assert(heap_is_empty(heap) == !n);
if (n > 0) {
assert(heap_max(heap) == heap->array[1]);
}
/* Check indexes. */
for (i = 1; i <= n; i++) {
assert(heap->array[i]->idx == i);
}
/* Check that priority values are internally consistent. */
for (i = 1; i <= n; i++) {
element = element_from_heap_node(heap->array[i]);
assert(element->heap_node.priority == (element->full_pri >> 16));
}
/* Check the heap property. */
for (i = 1; i <= n; i++) {
size_t parent = heap_parent__(i);
size_t left = heap_left__(i);
size_t right = heap_right__(i);
if (parent >= 1) {
assert(heap->array[parent]->priority >= heap->array[i]->priority);
}
if (left <= n) {
assert(heap->array[left]->priority <= heap->array[i]->priority);
}
if (right <= n) {
assert(heap->array[right]->priority <= heap->array[i]->priority);
}
}
/* Check that HEAP_FOR_EACH iterates all the nodes in order. */
i = 0;
HEAP_FOR_EACH (element, heap_node, heap) {
assert(i < n);
assert(&element->heap_node == heap->array[i + 1]);
i++;
}
assert(i == n);
priorities_copy = xmemdup(priorities, n * sizeof *priorities);
elements_copy = xmalloc(n * sizeof *priorities);
i = 0;
HEAP_FOR_EACH (element, heap_node, heap) {
elements_copy[i++] = element->heap_node.priority;
}
qsort(priorities_copy, n, sizeof *priorities_copy, compare_uint32s);
qsort(elements_copy, n, sizeof *elements_copy, compare_uint32s);
for (i = 0; i < n; i++) {
assert((priorities_copy[i] >> 16) == elements_copy[i]);
}
free(priorities_copy);
free(elements_copy);
}
static void
shuffle(uint32_t *p, size_t n)
{
for (; n > 1; n--, p++) {
uint32_t *q = &p[random_range(n)];
uint32_t tmp = *p;
*p = *q;
*q = tmp;
}
}
/* Prints the values in 'heap', plus 'name' as a title. */
static void OVS_UNUSED
print_heap(const char *name, struct heap *heap)
{
struct element *e;
printf("%s:", name);
HEAP_FOR_EACH (e, heap_node, heap) {
printf(" %"PRIu32":%"PRIu32, e->full_pri >> 16, e->full_pri & 0xffff);
}
printf("\n");
}
static int
factorial(int n_items)
{
int n, i;
n = 1;
for (i = 2; i <= n_items; i++) {
n *= i;
}
return n;
}
static void
swap(uint32_t *a, uint32_t *b)
{
uint32_t tmp = *a;
*a = *b;
*b = tmp;
}
static void
reverse(uint32_t *a, int n)
{
int i;
for (i = 0; i < n / 2; i++) {
int j = n - (i + 1);
swap(&a[i], &a[j]);
}
}
static bool
next_permutation(uint32_t *a, int n)
{
int k;
for (k = n - 2; k >= 0; k--) {
if ((a[k] >> 16) < (a[k + 1] >> 16)) {
int l;
for (l = n - 1; ; l--) {
if ((a[l] >> 16) > (a[k] >> 16)) {
swap(&a[k], &a[l]);
reverse(a + (k + 1), n - (k + 1));
return true;
}
}
}
}
return false;
}
static void
test_insert_delete__(struct element *elements,
const uint32_t *insert,
const uint32_t *delete,
size_t n)
{
struct heap heap;
size_t i;
heap_init(&heap);
check_heap(&heap, NULL, 0);
for (i = 0; i < n; i++) {
uint32_t priority = insert[i];
elements[i].full_pri = priority;
heap_insert(&heap, &elements[i].heap_node, priority >> 16);
check_heap(&heap, insert, i + 1);
}
for (i = 0; i < n; i++) {
struct element *element;
HEAP_FOR_EACH (element, heap_node, &heap) {
if (element->full_pri == delete[i]) {
goto found;
}
}
OVS_NOT_REACHED();
found:
heap_remove(&heap, &element->heap_node);
check_heap(&heap, delete + i + 1, n - (i + 1));
}
heap_destroy(&heap);
}
static void
test_insert_delete_raw__(struct element *elements,
const uint32_t *insert, unsigned int insert_pattern,
const uint32_t *delete, unsigned int delete_pattern,
size_t n)
{
struct heap heap;
size_t i;
heap_init(&heap);
check_heap(&heap, NULL, 0);
for (i = 0; i < n; i++) {
uint32_t priority = insert[i];
elements[i].full_pri = priority;
heap_raw_insert(&heap, &elements[i].heap_node, priority >> 16);
if (insert_pattern & (1u << i)) {
heap_rebuild(&heap);
check_heap(&heap, insert, i + 1);
}
}
for (i = 0; i < n; i++) {
struct element *element;
HEAP_FOR_EACH (element, heap_node, &heap) {
if (element->full_pri == delete[i]) {
goto found;
}
}
OVS_NOT_REACHED();
found:
heap_raw_remove(&heap, &element->heap_node);
if (delete_pattern & (1u << i)) {
heap_rebuild(&heap);
check_heap(&heap, delete + i + 1, n - (i + 1));
}
}
heap_destroy(&heap);
}
static void
test_heap_insert_delete_same_order(struct ovs_cmdl_context *ctx OVS_UNUSED)
{
enum { N_ELEMS = 7 };
uint32_t insert[N_ELEMS];
int n_permutations;
size_t i;
for (i = 0; i < N_ELEMS; i++) {
insert[i] = i << 16;
}
n_permutations = 0;
do {
struct element elements[N_ELEMS];
n_permutations++;
test_insert_delete__(elements, insert, insert, N_ELEMS);
} while (next_permutation(insert, N_ELEMS));
assert(n_permutations == factorial(N_ELEMS));
}
static void
test_heap_insert_delete_reverse_order(struct ovs_cmdl_context *ctx OVS_UNUSED)
{
enum { N_ELEMS = 7 };
uint32_t insert[N_ELEMS];
int n_permutations;
size_t i;
for (i = 0; i < N_ELEMS; i++) {
insert[i] = i << 16;
}
n_permutations = 0;
do {
struct element elements[N_ELEMS];
uint32_t delete[N_ELEMS];
n_permutations++;
for (i = 0; i < N_ELEMS; i++) {
delete[N_ELEMS - i - 1] = insert[i];
}
test_insert_delete__(elements, insert, delete, N_ELEMS);
} while (next_permutation(insert, N_ELEMS));
assert(n_permutations == factorial(N_ELEMS));
}
static void
test_heap_insert_delete_every_order(struct ovs_cmdl_context *ctx OVS_UNUSED)
{
enum { N_ELEMS = 5 };
uint32_t insert[N_ELEMS];
int outer_permutations;
size_t i;
for (i = 0; i < N_ELEMS; i++) {
insert[i] = i << 16;
}
outer_permutations = 0;
do {
struct element elements[N_ELEMS];
uint32_t delete[N_ELEMS];
int inner_permutations;
outer_permutations++;
for (i = 0; i < N_ELEMS; i++) {
delete[i] = i << 16;
}
inner_permutations = 0;
do {
inner_permutations++;
test_insert_delete__(elements, insert, delete, N_ELEMS);
} while (next_permutation(delete, N_ELEMS));
assert(inner_permutations == factorial(N_ELEMS));
} while (next_permutation(insert, N_ELEMS));
assert(outer_permutations == factorial(N_ELEMS));
}
static void
test_heap_insert_delete_same_order_with_dups(struct ovs_cmdl_context *ctx OVS_UNUSED)
{
enum { N_ELEMS = 7 };
unsigned int pattern;
size_t i;
for (pattern = 0; pattern < (1u << N_ELEMS); pattern += 2) {
int n_permutations, expected_permutations;
uint32_t insert[N_ELEMS];
int j;
j = 0;
for (i = 0; i < N_ELEMS; i++) {
if (i && !(pattern & (1u << i))) {
j++;
}
insert[i] = (j << 16) | i;
}
expected_permutations = factorial(N_ELEMS);
for (i = 0; i < N_ELEMS; ) {
j = i + 1;
if (pattern & (1u << i)) {
for (; j < N_ELEMS; j++) {
if (!(pattern & (1u << j))) {
break;
}
}
expected_permutations /= factorial(j - i + 1);
}
i = j;
}
n_permutations = 0;
do {
struct element elements[N_ELEMS];
n_permutations++;
test_insert_delete__(elements, insert, insert, N_ELEMS);
} while (next_permutation(insert, N_ELEMS));
assert(n_permutations == expected_permutations);
}
}
static void
test_heap_raw_insert(struct ovs_cmdl_context *ctx OVS_UNUSED)
{
enum { N_ELEMS = 7 };
uint32_t insert[N_ELEMS];
int n_permutations;
size_t i;
for (i = 0; i < N_ELEMS; i++) {
insert[i] = i << 16;
}
n_permutations = 0;
do {
struct element elements[N_ELEMS];
n_permutations++;
test_insert_delete_raw__(elements,
insert, 1u << (N_ELEMS - 1),
insert, UINT_MAX,
N_ELEMS);
} while (next_permutation(insert, N_ELEMS));
assert(n_permutations == factorial(N_ELEMS));
}
static void
test_heap_raw_delete(struct ovs_cmdl_context *ctx OVS_UNUSED)
{
enum { N_ELEMS = 16 };
uint32_t insert[N_ELEMS];
uint32_t delete[N_ELEMS];
size_t i;
for (i = 0; i < N_ELEMS; i++) {
insert[i] = i << 16;
delete[i] = i << 16;
}
for (i = 0; i < 1000; i++) {
struct element elements[N_ELEMS];
shuffle(insert, N_ELEMS);
shuffle(delete, N_ELEMS);
test_insert_delete_raw__(elements,
insert, 0,
delete,
(1u << (N_ELEMS - 1)) | (1u << (N_ELEMS / 2)),
N_ELEMS);
}
}
static const struct ovs_cmdl_command commands[] = {
{ "insert-delete-same-order", NULL, 0, 0,
test_heap_insert_delete_same_order, OVS_RO },
{ "insert-delete-reverse-order", NULL, 0, 0,
test_heap_insert_delete_reverse_order, OVS_RO },
{ "insert-delete-every-order", NULL, 0, 0,
test_heap_insert_delete_every_order, OVS_RO },
{ "insert-delete-same-order-with-dups", NULL, 0, 0,
test_heap_insert_delete_same_order_with_dups, OVS_RO },
{ "raw-insert", NULL, 0, 0, test_heap_raw_insert, OVS_RO },
{ "raw-delete", NULL, 0, 0, test_heap_raw_delete, OVS_RO },
{ NULL, NULL, 0, 0, NULL, OVS_RO },
};
static void
test_heap_main(int argc, char *argv[])
{
struct ovs_cmdl_context ctx = {
.argc = argc - 1,
.argv = argv + 1,
};
set_program_name(argv[0]);
ovs_cmdl_run_command(&ctx, commands);
}
OVSTEST_REGISTER("test-heap", test_heap_main);