For conducting Header Space Analysis (HSA), we convert the wildcarded
OpenFlow flow represented by 'struct match' into an encoded byte array.
To further save memory, we use a sparse array to represent such byte
array in the same way as 'struct miniflow'.  So, this commit implements
the structs and functions for conversion between sparse array and
'struct match'.

This commit also implements the set operations (e.g. intersect, union,
complementation and difference) as functions.

Signed-off-by: Alex Wang <al...@nicira.com>
---
 lib/automake.mk        |    2 +
 lib/hsa-match.c        |  374 ++++++++++++++++++++++++++++++++++++++++++++++++
 lib/hsa-match.h        |   92 ++++++++++++
 tests/automake.mk      |    2 +
 tests/library.at       |    5 +
 tests/test-hsa-match.c |  171 ++++++++++++++++++++++
 6 files changed, 646 insertions(+)
 create mode 100644 lib/hsa-match.c
 create mode 100644 lib/hsa-match.h
 create mode 100644 tests/test-hsa-match.c

diff --git a/lib/automake.mk b/lib/automake.mk
index 3629079..09b7a64 100644
--- a/lib/automake.mk
+++ b/lib/automake.mk
@@ -111,6 +111,8 @@ lib_libopenvswitch_la_SOURCES = \
        lib/mac-learning.h \
        lib/match.c \
        lib/match.h \
+       lib/hsa-match.c \
+       lib/hsa-match.h \
        lib/mcast-snooping.c \
        lib/mcast-snooping.h \
        lib/memory.c \
diff --git a/lib/hsa-match.c b/lib/hsa-match.c
new file mode 100644
index 0000000..3d69996
--- /dev/null
+++ b/lib/hsa-match.c
@@ -0,0 +1,374 @@
+/*
+ * Copyright (c) 2015 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.
+ */
+
+#include <config.h>
+#include "hsa-match.h"
+
+#include "util.h"
+
+/* Given 'hsbm' and 'match', initializes 'hsbm' with 'match'.
+ * Caller must guarantee, 'hsbm->values' is not assigned. */
+void
+hsbm_init(struct hsbm *hsbm, const struct match *match)
+{
+    uint32_t *flow = (uint32_t *) &match->flow;
+    uint32_t *wc = (uint32_t *) &match->wc;
+    size_t sz = 0;
+    size_t i, j;
+
+    hsbm->map = 0;
+    hsbm->values = NULL;
+
+    /* Encodes every 4 bytes from 'match' to to 8 bytes and sets the
+     * 'hsbm->map' and 'hsbm->values' correctly. */
+    for (i = 0; i < HSBM_VALUES_MAX; i++) {
+        uint64_t encoded_value = 0;
+
+        for (j = 0; j < 32; j++) {
+            uint8_t flow_bit = (flow[i] >> j) & 0x1;
+            uint8_t wc_bit = (wc[i] >> j) & 0x1;
+
+            /* If wc_bit is set, checks the bit.  Otherwise, sets to 'x'. */
+            if (wc_bit) {
+                encoded_value |= ((uint64_t) (flow_bit ? HSBM_VALUE_BIT_EM_ONE
+                                     : HSBM_VALUE_BIT_EM_ZERO) << (2*j));
+            } else {
+                encoded_value |= ((uint64_t) HSBM_VALUE_BIT_WC << (2*j));
+            }
+        }
+
+        if (encoded_value == UINT64_MAX) {
+            hsbm->map |= (uint64_t) 0x1 << i;
+        } else {
+            hsbm->values = xrealloc(hsbm->values,
+                                       (++sz) * sizeof *hsbm->values);
+            hsbm->values[sz-1] = encoded_value;
+        }
+    }
+}
+
+/* Initializes 'hsbm' such that only one bit 'map_idx' in 'hsbm->map' is
+ * HSBM_MAP_BIT_NOT_WC.  Correspondingly, sets the 'values[0]' so that
+ * the 'bit_idx' bit is set to 'bit_val'. */
+void
+hsbm_init_one_bit(struct hsbm *hsbm, size_t map_idx, size_t bit_idx,
+                  size_t bit_val)
+{
+    ovs_assert(map_idx < HSBM_VALUES_MAX);
+    hsbm->map = ((uint64_t) 1 << HSBM_VALUES_MAX) - 1;
+    hsbm->map &= ~((uint64_t) 1 << map_idx);
+    hsbm->values = xzalloc(sizeof *hsbm->values);
+    hsbm->values[0] = ~((uint64_t) HSBM_VALUE_BIT_WC << 2*bit_idx)
+        | ((uint64_t) bit_val << 2*bit_idx);
+}
+
+/* Frees the 'values' pointer if it is non-NULL. */
+void
+hsbm_uninit(struct hsbm *hsbm)
+{
+    if (hsbm->values) {
+        free(hsbm->values);
+    }
+}
+
+/* Destroys the 'hsbm'. */
+void
+hsbm_destroy(struct hsbm *hsbm)
+{
+    hsbm_uninit(hsbm);
+    free(hsbm);
+}
+
+/* Converts the 'hsbm' back to 'match'.  */
+void
+hsbm_to_match(struct match *match, const struct hsbm *hsbm)
+{
+    uint32_t *flow = (uint32_t *) &match->flow;
+    uint32_t *wc = (uint32_t *) &match->wc;
+    size_t idx = 0;
+    size_t i;
+
+    for (i = 0; i < HSBM_VALUES_MAX; i++) {
+        if ((hsbm->map >> i) & 0x1) {
+            flow[i] = wc[i] = 0;
+        } else {
+            uint64_t encoded_value = hsbm->values[idx++];
+            uint32_t flow_value = 0;
+            uint32_t wc_value = 0;
+            size_t j;
+
+            for (j = 0; j < 32; j++) {
+                switch ((encoded_value >> (2*j)) & 0x3) {
+                /* wildcard unmasked (don't care), sets wc bit to '0'. */
+                case HSBM_VALUE_BIT_WC:
+                    wc_value = wc_value | ((uint32_t) 0x0 << j);
+                    break;
+                /* exact match '1'. */
+                case HSBM_VALUE_BIT_EM_ONE:
+                    flow_value = flow_value | ((uint32_t) 0x1 << j);
+                    wc_value = wc_value | ((uint32_t) 0x01 << j);
+                    break;
+                /* exact match '0'. */
+                case HSBM_VALUE_BIT_EM_ZERO:
+                    flow_value = flow_value | ((uint32_t) 0x0 << j);
+                    wc_value = wc_value | ((uint32_t) 0x1 << j);
+                    break;
+                /* no intersection, error! */
+                default:
+                    ovs_assert(false);
+                }
+            }
+            flow[i] = flow_value;
+            wc[i] = wc_value;
+        }
+    }
+}
+
+/* Returns true if 'hsbm1' is a subset of 'hsbm2'. */
+bool
+hsbm_check_subset(const struct hsbm *hsbm1,
+                     const struct hsbm *hsbm2)
+{
+    uint64_t *vals1 = hsbm1->values;
+    uint64_t *vals2 = hsbm2->values;
+    size_t i;
+
+    for (i = 0; i < HSBM_VALUES_MAX; i++) {
+        uint8_t bit1 = hsbm1->map >> i & 0x1;
+        uint8_t bit2 = hsbm2->map >> i & 0x1;
+
+        if (bit1 == HSBM_MAP_BIT_WC && bit2 == HSBM_MAP_BIT_WC) {
+            /* Do nothing. */
+        } else if (bit1 == HSBM_MAP_BIT_WC && bit2 == HSBM_MAP_BIT_NOT_WC) {
+            /* 'hsbm2' is more specific, 'hsbm1' cannot be its subset. */
+            return false;
+        } else if (bit1 == HSBM_MAP_BIT_NOT_WC && bit2 == HSBM_MAP_BIT_WC) {
+            /* 'hsbm1' is more specific, skips the exact value. */
+            vals1++;
+        } else {
+            /* if both have specific values at index i, compares the values. */
+            if (*vals1++ & ~(*vals2++)) {
+                return false;
+            }
+        }
+    }
+
+    return true;
+}
+
+/* Creates and returns a 'hsbm_list'. */
+struct hsbm_list *
+hsbm_list_create(void)
+{
+    struct hsbm_list *ret = xmalloc(sizeof *ret);
+
+    list_init(&ret->list);
+
+    return ret;
+}
+
+/* Destroys the 'hsbm_list' and all its elements. */
+void
+hsbm_list_destroy(struct hsbm_list *hsbm_list)
+{
+    struct hsbm *iter, *next;
+
+    LIST_FOR_EACH_SAFE (iter, next, list_node, &hsbm_list->list) {
+        list_remove(&iter->list_node);
+        hsbm_destroy(iter);
+    }
+    free(hsbm_list);
+}
+
+/* Inserts the 'hsbm' to 'hsbm_list' while removing all duplicate, superset
+ * and subset.  This function will take ownership of 'hsbm'. */
+void
+hsbm_insert_without_duplicate(struct hsbm_list *hsbm_list, struct hsbm *hsbm)
+{
+    struct hsbm *comp, *next;
+
+    LIST_FOR_EACH_SAFE (comp, next, list_node, &hsbm_list->list) {
+        bool is_subset = hsbm_check_subset(hsbm, comp);
+        bool is_superset = hsbm_check_subset(comp, hsbm);
+
+        if (is_subset) {
+            hsbm_destroy(hsbm);
+
+            return;
+        } else if (is_superset) {
+            /* If the to-be-inserted 'hsbm' is a superset of
+             * existing element, removes the existing one. */
+            list_remove(&comp->list_node);
+            hsbm_destroy(comp);
+        }
+    }
+    list_insert(&hsbm_list->list, &hsbm->list_node);
+}
+
+/* Given the 'hsbm', returns the complement of 'hsbm' as a list (union). */
+struct hsbm_list *
+hsbm_complement(struct hsbm *hsbm)
+{
+    struct hsbm_list *result = hsbm_list_create();
+    uint64_t *values = hsbm->values;
+    size_t i;
+
+    for (i = 0; i < HSBM_VALUES_MAX; i++) {
+        uint8_t map_bit = hsbm->map >> i & 0x1;
+
+        if (map_bit == 0) {
+            size_t j;
+
+            for (j = 0; j < 32; j++) {
+                struct hsbm *flap = NULL;
+
+                /* If a non-wildcarded bit is found, creates a flap. */
+                if (((*values >> 2*j) & 0x3) == HSBM_VALUE_BIT_EM_ZERO) {
+                    flap = xmalloc(sizeof *flap);
+                    hsbm_init_one_bit(flap, i, j, HSBM_VALUE_BIT_EM_ONE);
+                } else if (((*values >> 2*j) & 0x3) == HSBM_VALUE_BIT_EM_ONE) {
+                    flap = xmalloc(sizeof *flap);
+                    hsbm_init_one_bit(flap, i, j, HSBM_VALUE_BIT_EM_ZERO);
+                }
+                if (flap) {
+                    list_insert(&result->list, &flap->list_node);
+                }
+            }
+            /* Jumps to next value. */
+            values++;
+        }
+    }
+
+    return result;
+}
+
+/* Given two 'hsbm's, returns the intersection of them.
+ * Returns NULL when the intersection is empty. */
+struct hsbm *
+hsbm_intersect(struct hsbm *comp_1, struct hsbm *comp_2)
+{
+    struct hsbm *result = xmalloc(sizeof *result);
+    uint64_t *vals_1 = comp_1->values;
+    uint64_t *vals_2 = comp_2->values;
+    uint64_t *vals_result;
+    size_t n_vals = 0;
+    size_t i, j;
+
+    result->map = comp_1->map & comp_2->map;
+    for (i = 0; i < HSBM_VALUES_MAX; i++) {
+        if ((result->map >> i & 0x1) == 0) {
+            n_vals++;
+        }
+    }
+    vals_result = result->values = xmalloc(n_vals * sizeof *result->values);
+
+    for (i = 0; i < HSBM_VALUES_MAX; i++) {
+        uint8_t map_bit_1 = comp_1->map >> i & 0x1;
+        uint8_t map_bit_2 = comp_2->map >> i & 0x1;
+
+        if (map_bit_1 == HSBM_MAP_BIT_WC && map_bit_2 == HSBM_MAP_BIT_WC) {
+            /* Do nothing. */
+        } else if (map_bit_1 == HSBM_MAP_BIT_WC
+                   && map_bit_2 == HSBM_MAP_BIT_NOT_WC) {
+            *vals_result++ = *vals_2++;
+        } else if (map_bit_1 == HSBM_MAP_BIT_NOT_WC
+                   && map_bit_2 == HSBM_MAP_BIT_WC) {
+            *vals_result++ = *vals_1++;
+        } else {
+            uint64_t val = *vals_1++ & *vals_2++;
+
+            for (j = 0; j < 32; j++) {
+                /* If intersection results in empty, returns NULL. */
+                if (((val >> 2*j) & 0x3) == 0) {
+                    hsbm_destroy(result);
+
+                    return NULL;
+                }
+            }
+            *vals_result++ = val;
+        }
+    }
+
+    return result;
+}
+
+/* Given two 'hsbm's, calculates the diff (hsbm_1 - hsbm_2) and returns
+ * the result in 'hsbm_list'. */
+struct hsbm_list *
+hsbm_diff(struct hsbm *hsbm_1, struct hsbm *hsbm_2)
+{
+    struct hsbm_list *result = hsbm_list_create();
+    struct hsbm_list *complement;
+    struct hsbm *iter;
+
+    complement = hsbm_complement(hsbm_2);
+
+    LIST_FOR_EACH (iter, list_node, &complement->list) {
+        struct hsbm *intersect = hsbm_intersect(hsbm_1, iter);
+
+        if (intersect) {
+            list_insert(&result->list, &intersect->list_node);
+        }
+    }
+    hsbm_list_destroy(complement);
+
+    return result;
+}
+
+/* Given the 'hsbm_list', checks if 'hsbm' can still find a match
+ * (non-NULL intersection) in 'hsbm_list'.  Returns true if a match
+ * can be found, otherwise, false. */
+bool
+hsbm_list_check_hsbm(struct hsbm_list *hsbm_list, struct hsbm *hsbm)
+{
+    struct hsbm *iter;
+    bool ret = false;
+
+    LIST_FOR_EACH (iter, list_node, &hsbm_list->list) {
+        struct hsbm *intersect = hsbm_intersect(iter, hsbm);
+
+        if (intersect) {
+            hsbm_destroy(intersect);
+            ret = true;
+            break;
+        }
+    }
+
+    return ret;
+}
+
+/* Subtracts 'hsbm' from each element of 'hsbm_list', returns the result
+ * in another hsbm_list.  The function will take ownership of 'hsbm_list'. */
+struct hsbm_list *
+hsbm_list_apply_hsbm(struct hsbm_list *hsbm_list, struct hsbm *hsbm)
+{
+    struct hsbm_list *result = hsbm_list_create();
+    struct hsbm *iter;
+
+    LIST_FOR_EACH (iter, list_node, &hsbm_list->list) {
+        struct hsbm_list *diff = hsbm_diff(iter, hsbm);
+        struct hsbm *tmp, *next;
+
+        LIST_FOR_EACH_SAFE (tmp, next, list_node, &diff->list) {
+            list_remove(&tmp->list_node);
+            hsbm_insert_without_duplicate(result, tmp);
+        }
+        hsbm_list_destroy(diff);
+    }
+    hsbm_list_destroy(hsbm_list);
+
+    return result;
+}
diff --git a/lib/hsa-match.h b/lib/hsa-match.h
new file mode 100644
index 0000000..63fabd4
--- /dev/null
+++ b/lib/hsa-match.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2015 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.
+ */
+
+#ifndef HSA_MATCH_H
+#define HSA_MATCH_H 1
+
+#include "flow.h"
+#include "list.h"
+#include "match.h"
+
+/*
+ * For conducting Header Space Analysis (HSA), we convert the wildcarded
+ * OpenFlow flow represented by 'struct match' into a byte array.
+ * Specifically, each bit in the OpenFlow flow is encoded into two bits:
+ *
+ *    exact match 0 -> 01
+ *    exact match 1 -> 10
+ *    wildcard    * -> 11
+ *    empty         -> 00
+ *
+ * We use '00' to indicate the empty bit resulted when intersecting two
+ * OpenFlow flows with exact match '0' and exact match '1' at same index.
+ *
+ * This conversion will generate a struct same size as 'struct match'.
+ * To save more space, we will use a sparse array to represent such byte
+ * array in the same way as 'struct miniflow'.
+ */
+#define HSBM_MAP_BIT_WC          1
+#define HSBM_MAP_BIT_NOT_WC      0
+
+#define HSBM_VALUE_BIT_EMPTY     0x0
+#define HSBM_VALUE_BIT_EM_ZERO   0x1
+#define HSBM_VALUE_BIT_EM_ONE    0x2
+#define HSBM_VALUE_BIT_WC        0x3
+
+/* A sparse representation of a byte array derived from "struct match".
+ *
+ * The 'map' member holds one bit for each uint64_t in the byte array.  Each
+ * 1-bit indicates that the corresponding uint64_t is UINT64_MAX (i.e. all
+ * HSBM_VALUE_BIT_WC), each 0-bit that it is not all-one.
+ *
+ * The 'values' member is an array that has one element for each 0-bit in
+ * 'map'.  The least-numbered 0-bit is in the first element of 'values', the
+ * next 0-bit is in the next array element, and so on.
+ */
+struct hsbm {
+    struct ovs_list list_node;  /* In 'hsbm_list'. */
+    uint64_t map;
+    uint64_t *values;
+};
+
+/* Based on the description above, the maximum size of 'hsbm->values'
+ * is the double of FLOW_U64S. */
+#define HSBM_VALUES_MAX (FLOW_U64S * 2)
+BUILD_ASSERT_DECL(HSBM_VALUES_MAX < 64);
+
+void hsbm_init(struct hsbm *, const struct match *);
+void hsbm_init_one_bit(struct hsbm *, size_t map_idx, size_t bit_idx,
+                       size_t bit_val);
+void hsbm_uninit(struct hsbm *);
+void hsbm_destroy(struct hsbm *);
+void hsbm_to_match(struct match *, const struct hsbm *);
+bool hsbm_check_subset(const struct hsbm *, const struct hsbm *);
+
+/* List of 'struct hsbm's. */
+struct hsbm_list {
+    struct ovs_list list;
+};
+
+struct hsbm_list *hsbm_list_create(void);
+void hsbm_list_destroy(struct hsbm_list *);
+void hsbm_insert_without_duplicate(struct hsbm_list *, struct hsbm *);
+struct hsbm_list *hsbm_complement(struct hsbm *);
+struct hsbm *hsbm_intersect(struct hsbm *, struct hsbm *);
+struct hsbm_list *hsbm_diff(struct hsbm *, struct hsbm *);
+bool hsbm_list_check_hsbm(struct hsbm_list *, struct hsbm *);
+struct hsbm_list *hsbm_list_apply_hsbm(struct hsbm_list *, struct hsbm *);
+
+#endif /* hsa-match.h */
diff --git a/tests/automake.mk b/tests/automake.mk
index abbfcb5..43cace9 100644
--- a/tests/automake.mk
+++ b/tests/automake.mk
@@ -140,6 +140,7 @@ valgrind_wrappers = \
        tests/valgrind/test-list \
        tests/valgrind/test-lockfile \
        tests/valgrind/test-multipath \
+       tests/valgrind/test-hsa-match \
        tests/valgrind/test-odp \
        tests/valgrind/test-ovsdb \
        tests/valgrind/test-packets \
@@ -270,6 +271,7 @@ tests_ovstest_SOURCES = \
        tests/test-list.c \
        tests/test-lockfile.c \
        tests/test-multipath.c \
+       tests/test-hsa-match.c \
        tests/test-netflow.c \
        tests/test-odp.c \
        tests/test-packets.c \
diff --git a/tests/library.at b/tests/library.at
index fab7402..8e2b711 100644
--- a/tests/library.at
+++ b/tests/library.at
@@ -209,3 +209,8 @@ AT_CLEANUP
 AT_SETUP([use of public headers])
 AT_CHECK([test-lib], [0], [])
 AT_CLEANUP
+
+AT_SETUP([test hsa match])
+AT_CHECK([ovstest test-hsa-match], [0], [....
+])
+AT_CLEANUP
\ No newline at end of file
diff --git a/tests/test-hsa-match.c b/tests/test-hsa-match.c
new file mode 100644
index 0000000..041c85f
--- /dev/null
+++ b/tests/test-hsa-match.c
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2015 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.
+ */
+
+#include <config.h>
+#undef NDEBUG
+#include "hsa-match.h"
+#include "util.h"
+#include <assert.h>
+#include "ovstest.h"
+
+static void
+test_init(void)
+{
+    struct hsbm hsbm, hsbm_one_bit;
+    struct match match, result;
+    uint64_t map, val, val0, val1;
+
+    /* Sets some fields. */
+    memset(&match, 0, sizeof match);
+    memset(&result, 0, sizeof result);
+    match_set_tun_id_masked(&match, 0x0123, 0xFFFF);
+    match_set_reg(&match, 3, 0x1);
+
+    /* Expected results, please check lib/hsa-match.h for encoding details. */
+    map = UINT64_MAX & (((uint64_t) 1 << HSBM_VALUES_MAX) - 1);
+    map = map & ~((uint64_t) 1 << (offsetof(struct match, flow.tunnel.tun_id) 
/ 4))
+        & ~((uint64_t) 1 << (offsetof(struct match, flow.regs[3]) / 4));
+    val0 = (uint64_t) 0xFFFFFFFF5556595A;
+    val1 = (uint64_t) 0x5555555555555556;
+
+    /* Checks the conversion. */
+    hsbm_init(&hsbm, &match);
+    assert(hsbm.map == map && hsbm.values[0] == val0 && hsbm.values[1] == 
val1);
+    hsbm_to_match(&result, &hsbm);
+    assert(match_equal(&match, &result));
+
+    /* Tests just set one bit.  */
+    hsbm_init_one_bit(&hsbm_one_bit, 10, 10, HSBM_VALUE_BIT_EM_ONE);
+    map = UINT64_MAX & (((uint64_t) 1 << HSBM_VALUES_MAX) - 1);
+    map = map & ~((uint64_t) 1 << 10);
+    val = ~((uint64_t) 0x3 << 20) | ((uint64_t) HSBM_VALUE_BIT_EM_ONE << 20);
+    assert(hsbm_one_bit.map == map && hsbm_one_bit.values[0] == val);
+
+    /* Cleans up. */
+    hsbm_uninit(&hsbm);
+    hsbm_uninit(&hsbm_one_bit);
+}
+
+static void
+test_check_subset(void)
+{
+    struct hsbm hsbm_superset, hsbm_subset;
+    struct match match_superset, match_subset;
+
+    memset(&match_superset, 0, sizeof match_superset);
+    memset(&match_subset, 0, sizeof match_subset);
+    /* Sets superset fields. */
+    match_set_tun_id_masked(&match_superset, 0x1200, 0xFF00);
+    match_set_reg_masked(&match_superset, 3, 0x00FF0000, 0x00FF0000);
+    /* Sets subset fields. */
+    match_set_tun_id_masked(&match_subset, 0x1234, 0xFFFF);
+    match_set_reg_masked(&match_subset, 3, 0x12FF3200, 0xFFFFFF00);
+
+    hsbm_init(&hsbm_superset, &match_superset);
+    hsbm_init(&hsbm_subset, &match_subset);
+
+    /* Checks subset. */
+    assert(hsbm_check_subset(&hsbm_subset, &hsbm_superset));
+    hsbm_uninit(&hsbm_superset);
+    hsbm_uninit(&hsbm_subset);
+}
+
+static void
+test_hsbm_complement(void)
+{
+    struct hsbm_list *result;
+    struct match match;
+    struct hsbm hsbm;
+    struct hsbm *iter;
+    size_t bit_idx = 0;
+
+    /* Sets just one field to all zero. */
+    memset(&match, 0, sizeof match);
+    match_set_reg(&match, 3, 0);
+    hsbm_init(&hsbm, &match);
+
+    result = hsbm_complement(&hsbm);
+
+    assert(list_size(&result->list) == 32);
+    LIST_FOR_EACH (iter, list_node, &result->list) {
+        assert(iter->map == hsbm.map);
+        assert(iter->values[0] ==
+               (~((uint64_t) HSBM_VALUE_BIT_WC << 2*bit_idx)
+                | ((uint64_t) HSBM_VALUE_BIT_EM_ONE << 2*bit_idx)));
+        bit_idx++;
+    }
+
+    hsbm_list_destroy(result);
+    hsbm_uninit(&hsbm);
+}
+
+static void
+test_hsbm_intersect(void)
+{
+    struct match match1, match2;
+    struct hsbm hsbm1, hsbm2;
+    struct hsbm *result;
+    uint64_t map, val0, val1, val2, val3;
+
+    memset(&match1, 0, sizeof match1);
+    match_set_tun_id_masked(&match1, 0x0123, 0xFFFF);
+    match_set_reg(&match1, 3, 0x1);
+    hsbm_init(&hsbm1, &match1);
+
+    memset(&match2, 0, sizeof match2);
+    match_set_tun_id_masked(&match2, 0xABCD000000000000, 0xFFFF000000000000);
+    match_set_reg(&match2, 4, 0);
+    hsbm_init(&hsbm2, &match2);
+
+    map = UINT64_MAX & (((uint64_t) 1 << HSBM_VALUES_MAX) - 1);
+    map = map
+        & ~((uint64_t) 1 << (offsetof(struct match, flow.tunnel.tun_id) / 4))
+        & ~((uint64_t) 1 << (offsetof(struct match, flow.tunnel.tun_id) / 4 + 
1))
+        & ~((uint64_t) 1 << (offsetof(struct match, flow.regs[3]) / 4))
+        & ~((uint64_t) 1 << (offsetof(struct match, flow.regs[4]) / 4));
+    val0 = (uint64_t) 0xFFFFFFFF5556595A;
+    val1 = (uint64_t) 0x999AA5A6FFFFFFFF;
+    val2 = (uint64_t) 0x5555555555555556;
+    val3 = (uint64_t) 0x5555555555555555;
+
+    result = hsbm_intersect(&hsbm1, &hsbm2);
+    assert(result->map == map && result->values[0] == val0
+           && result->values[1] == val1 && result->values[2] == val2
+           && result->values[3] == val3);
+
+    hsbm_uninit(&hsbm1);
+    hsbm_uninit(&hsbm2);
+    hsbm_destroy(result);
+}
+
+static void
+run_test(void (*function)(void))
+{
+    function();
+    printf(".");
+}
+
+static void
+test_hsa_match_main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+{
+    run_test(test_init);
+    run_test(test_check_subset);
+    run_test(test_hsbm_complement);
+    run_test(test_hsbm_intersect);
+    printf("\n");
+}
+
+OVSTEST_REGISTER("test-hsa-match", test_hsa_match_main);
-- 
1.7.9.5

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to