LCOV - code coverage report
Current view: top level - tests - test-hash.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 94 114 82.5 %
Date: 2016-09-14 01:02:56 Functions: 13 13 100.0 %
Branches: 45 56 80.4 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Copyright (c) 2009, 2012, 2014, 2015 Nicira, Inc.
       3                 :            :  *
       4                 :            :  * Licensed under the Apache License, Version 2.0 (the "License");
       5                 :            :  * you may not use this file except in compliance with the License.
       6                 :            :  * You may obtain a copy of the License at:
       7                 :            :  *
       8                 :            :  *     http://www.apache.org/licenses/LICENSE-2.0
       9                 :            :  *
      10                 :            :  * Unless required by applicable law or agreed to in writing, software
      11                 :            :  * distributed under the License is distributed on an "AS IS" BASIS,
      12                 :            :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      13                 :            :  * See the License for the specific language governing permissions and
      14                 :            :  * limitations under the License.
      15                 :            :  */
      16                 :            : 
      17                 :            : #include <config.h>
      18                 :            : #undef NDEBUG
      19                 :            : #include "hash.h"
      20                 :            : #include <assert.h>
      21                 :            : #include <inttypes.h>
      22                 :            : #include <stdio.h>
      23                 :            : #include <stdlib.h>
      24                 :            : #include <string.h>
      25                 :            : #include "jhash.h"
      26                 :            : #include "ovstest.h"
      27                 :            : 
      28                 :            : static void
      29                 :      27936 : set_bit(uint32_t array[3], int bit)
      30                 :            : {
      31 [ +  - ][ -  + ]:      27936 :     assert(bit >= 0 && bit <= 96);
      32                 :      27936 :     memset(array, 0, sizeof(uint32_t) * 3);
      33         [ +  + ]:      27936 :     if (bit < 96) {
      34                 :      27744 :         array[bit / 32] = UINT32_C(1) << (bit % 32);
      35                 :            :     }
      36                 :      27936 : }
      37                 :            : 
      38                 :            : /* When bit == n_bits, the function just 0 sets the 'values'. */
      39                 :            : static void
      40                 :    2110788 : set_bit128(ovs_u128 *values, int bit, int n_bits)
      41                 :            : {
      42 [ +  - ][ -  + ]:    2110788 :     assert(bit >= 0 && bit <= 2048);
      43                 :    2110788 :     memset(values, 0, n_bits/8);
      44         [ +  + ]:    2110788 :     if (bit < n_bits) {
      45                 :    2108608 :         int b = bit % 128;
      46                 :            : 
      47         [ +  + ]:    2108608 :         if (b < 64) {
      48                 :    1019488 :             values[bit / 128].u64.lo = UINT64_C(1) << (b % 64);
      49                 :            :         } else {
      50                 :    1089120 :             values[bit / 128].u64.hi = UINT64_C(1) << (b % 64);
      51                 :            :         }
      52                 :            :     }
      53                 :    2110788 : }
      54                 :            : 
      55                 :            : static uint64_t
      56                 :    1799808 : get_range128(ovs_u128 *value, int ofs, uint64_t mask)
      57                 :            : {
      58         [ +  + ]:    3599616 :     return ((ofs < 64 ? (value->u64.lo >> ofs) : 0) & mask)
      59         [ +  + ]:    1799808 :         | ((ofs <= 64 ? (value->u64.hi << (64 - ofs)) : (value->u64.hi >> (ofs - 64)) & mask));
      60                 :            : }
      61                 :            : 
      62                 :            : static uint32_t
      63                 :       1056 : hash_words_cb(uint32_t input)
      64                 :            : {
      65                 :       1056 :     return hash_words(&input, 1, 0);
      66                 :            : }
      67                 :            : 
      68                 :            : static uint32_t
      69                 :       1056 : jhash_words_cb(uint32_t input)
      70                 :            : {
      71                 :       1056 :     return jhash_words(&input, 1, 0);
      72                 :            : }
      73                 :            : 
      74                 :            : static uint32_t
      75                 :       1056 : hash_int_cb(uint32_t input)
      76                 :            : {
      77                 :       1056 :     return hash_int(input, 0);
      78                 :            : }
      79                 :            : 
      80                 :            : static void
      81                 :          3 : check_word_hash(uint32_t (*hash)(uint32_t), const char *name,
      82                 :            :                 int min_unique)
      83                 :            : {
      84                 :            :     int i, j;
      85                 :            : 
      86         [ +  + ]:        102 :     for (i = 0; i <= 32; i++) {
      87         [ +  + ]:         99 :         uint32_t in1 = i < 32 ? UINT32_C(1) << i : 0;
      88         [ +  + ]:       1683 :         for (j = i + 1; j <= 32; j++) {
      89         [ +  + ]:       1584 :             uint32_t in2 = j < 32 ? UINT32_C(1) << j : 0;
      90                 :       1584 :             uint32_t out1 = hash(in1);
      91                 :       1584 :             uint32_t out2 = hash(in2);
      92                 :       1584 :             const uint32_t unique_mask = (UINT32_C(1) << min_unique) - 1;
      93                 :            :             int ofs;
      94         [ +  + ]:      34320 :             for (ofs = 0; ofs < 32 - min_unique; ofs++) {
      95                 :      32736 :                 uint32_t bits1 = (out1 >> ofs) & unique_mask;
      96                 :      32736 :                 uint32_t bits2 = (out2 >> ofs) & unique_mask;
      97         [ -  + ]:      32736 :                 if (bits1 == bits2) {
      98                 :          0 :                     printf("Partial collision for '%s':\n", name);
      99                 :          0 :                     printf("%s(%08"PRIx32") = %08"PRIx32"\n", name, in1, out1);
     100                 :          0 :                     printf("%s(%08"PRIx32") = %08"PRIx32"\n", name, in2, out2);
     101                 :          0 :                     printf("%d bits of output starting at bit %d "
     102                 :            :                            "are both 0x%"PRIx32"\n", min_unique, ofs, bits1);
     103                 :            :                 }
     104                 :            :             }
     105                 :            :         }
     106                 :            :     }
     107                 :          3 : }
     108                 :            : 
     109                 :            : static void
     110                 :          2 : check_3word_hash(uint32_t (*hash)(const uint32_t[], size_t, uint32_t),
     111                 :            :                  const char *name)
     112                 :            : {
     113                 :            :     int i, j;
     114                 :            : 
     115         [ +  + ]:        196 :     for (i = 0; i <= 96; i++) {
     116         [ +  + ]:       9506 :         for (j = i + 1; j <= 96; j++) {
     117                 :            :             uint32_t in0[3], in1[3], in2[3];
     118                 :            :             uint32_t out0,out1, out2;
     119                 :       9312 :             const int min_unique = 12;
     120                 :       9312 :             const uint32_t unique_mask = (UINT32_C(1) << min_unique) - 1;
     121                 :            : 
     122                 :       9312 :             set_bit(in0, i);
     123                 :       9312 :             set_bit(in1, i);
     124                 :       9312 :             set_bit(in2, j);
     125                 :       9312 :             out0 = hash(in0, 3, 0);
     126                 :       9312 :             out1 = hash(in1, 3, 0);
     127                 :       9312 :             out2 = hash(in2, 3, 0);
     128                 :            : 
     129         [ -  + ]:       9312 :             if (out0 != out1) {
     130                 :          0 :                 printf("%s hash not the same for non-64 aligned data "
     131                 :            :                        "%08"PRIx32" != %08"PRIx32"\n", name, out0, out1);
     132                 :            :             }
     133         [ -  + ]:       9312 :             if ((out1 & unique_mask) == (out2 & unique_mask)) {
     134                 :          0 :                 printf("%s has a partial collision:\n", name);
     135                 :          0 :                 printf("hash(1 << %d) == %08"PRIx32"\n", i, out1);
     136                 :          0 :                 printf("hash(1 << %d) == %08"PRIx32"\n", j, out2);
     137                 :          0 :                 printf("The low-order %d bits of output are both "
     138                 :            :                        "0x%"PRIx32"\n", min_unique, out1 & unique_mask);
     139                 :            :             }
     140                 :            :         }
     141                 :            :     }
     142                 :          2 : }
     143                 :            : 
     144                 :            : static void
     145                 :          1 : check_hash_bytes128(void (*hash)(const void *, size_t, uint32_t, ovs_u128 *),
     146                 :            :                     const char *name, const int min_unique)
     147                 :            : {
     148                 :          1 :     const uint64_t unique_mask = (UINT64_C(1) << min_unique) - 1;
     149                 :          1 :     const int n_bits = sizeof(ovs_u128) * 8;
     150                 :            :     int i, j;
     151                 :            : 
     152         [ +  + ]:        130 :     for (i = 0; i <= n_bits; i++) {
     153                 :            :         OVS_PACKED(struct offset_ovs_u128 {
     154                 :            :             uint32_t a;
     155                 :            :             ovs_u128 b;
     156                 :            :         }) in0_data;
     157                 :            :         ovs_u128 *in0, in1;
     158                 :            :         ovs_u128 out0, out1;
     159                 :            : 
     160                 :        129 :         in0 = &in0_data.b;
     161                 :        129 :         set_bit128(in0, i, n_bits);
     162                 :        129 :         set_bit128(&in1, i, n_bits);
     163                 :        129 :         hash(in0, sizeof(ovs_u128), 0, &out0);
     164                 :        129 :         hash(&in1, sizeof(ovs_u128), 0, &out1);
     165         [ -  + ]:        129 :         if (!ovs_u128_equals(out0, out1)) {
     166                 :          0 :             printf("%s hash not the same for non-64 aligned data "
     167                 :            :                    "%016"PRIx64"%016"PRIx64" != %016"PRIx64"%016"PRIx64"\n",
     168                 :            :                    name, out0.u64.lo, out0.u64.hi, out1.u64.lo, out1.u64.hi);
     169                 :            :         }
     170                 :            : 
     171         [ +  + ]:       8385 :         for (j = i + 1; j <= n_bits; j++) {
     172                 :            :             ovs_u128 in2;
     173                 :            :             ovs_u128 out2;
     174                 :            :             int ofs;
     175                 :            : 
     176                 :       8256 :             set_bit128(&in2, j, n_bits);
     177                 :       8256 :             hash(&in2, sizeof(ovs_u128), 0, &out2);
     178         [ +  + ]:     908160 :             for (ofs = 0; ofs < 128 - min_unique; ofs++) {
     179                 :     899904 :                 uint64_t bits1 = get_range128(&out1, ofs, unique_mask);
     180                 :     899904 :                 uint64_t bits2 = get_range128(&out2, ofs, unique_mask);
     181                 :            : 
     182         [ -  + ]:     899904 :                 if (bits1 == bits2) {
     183                 :          0 :                     printf("%s has a partial collision:\n", name);
     184                 :          0 :                     printf("hash(1 << %d) == %016"PRIx64"%016"PRIx64"\n",
     185                 :            :                            i, out1.u64.hi, out1.u64.lo);
     186                 :          0 :                     printf("hash(1 << %d) == %016"PRIx64"%016"PRIx64"\n",
     187                 :            :                            j, out2.u64.hi, out2.u64.lo);
     188                 :          0 :                     printf("%d bits of output starting at bit %d "
     189                 :            :                            "are both 0x%016"PRIx64"\n", min_unique, ofs, bits1);
     190                 :            :                 }
     191                 :            :             }
     192                 :            :         }
     193                 :            :     }
     194                 :          1 : }
     195                 :            : 
     196                 :            : static void
     197                 :          1 : check_256byte_hash(void (*hash)(const void *, size_t, uint32_t, ovs_u128 *),
     198                 :            :                    const char *name, const int min_unique)
     199                 :            : {
     200                 :          1 :     const uint64_t unique_mask = (UINT64_C(1) << min_unique) - 1;
     201                 :          1 :     const int n_bits = sizeof(ovs_u128) * 8 * 16;
     202                 :            :     int i, j;
     203                 :            : 
     204         [ +  + ]:       2050 :     for (i = 0; i <= n_bits; i++) {
     205                 :            :         OVS_PACKED(struct offset_ovs_u128 {
     206                 :            :             uint32_t a;
     207                 :            :             ovs_u128 b[16];
     208                 :            :         }) in0_data;
     209                 :            :         ovs_u128 *in0, in1[16];
     210                 :            :         ovs_u128 out0, out1;
     211                 :            : 
     212                 :       2049 :         in0 = in0_data.b;
     213                 :       2049 :         set_bit128(in0, i, n_bits);
     214                 :       2049 :         set_bit128(in1, i, n_bits);
     215                 :       2049 :         hash(in0, sizeof(ovs_u128) * 16, 0, &out0);
     216                 :       2049 :         hash(in1, sizeof(ovs_u128) * 16, 0, &out1);
     217         [ -  + ]:       2049 :         if (!ovs_u128_equals(out0, out1)) {
     218                 :          0 :             printf("%s hash not the same for non-64 aligned data "
     219                 :            :                    "%016"PRIx64"%016"PRIx64" != %016"PRIx64"%016"PRIx64"\n",
     220                 :            :                    name, out0.u64.lo, out0.u64.hi, out1.u64.lo, out1.u64.hi);
     221                 :            :         }
     222                 :            : 
     223         [ +  + ]:    2100225 :         for (j = i + 1; j <= n_bits; j++) {
     224                 :            :             ovs_u128 in2[16];
     225                 :            :             ovs_u128 out2;
     226                 :            : 
     227                 :    2098176 :             set_bit128(in2, j, n_bits);
     228                 :    2098176 :             hash(in2, sizeof(ovs_u128) * 16, 0, &out2);
     229         [ -  + ]:    2098176 :             if ((out1.u64.lo & unique_mask) == (out2.u64.lo & unique_mask)) {
     230                 :          0 :                 printf("%s has a partial collision:\n", name);
     231                 :          0 :                 printf("hash(1 << %4d) == %016"PRIx64"%016"PRIx64"\n", i,
     232                 :            :                        out1.u64.hi, out1.u64.lo);
     233                 :          0 :                 printf("hash(1 << %4d) == %016"PRIx64"%016"PRIx64"\n", j,
     234                 :            :                        out2.u64.hi, out2.u64.lo);
     235                 :          0 :                 printf("The low-order %d bits of output are both "
     236                 :          0 :                        "0x%"PRIx64"\n", min_unique, out1.u64.lo & unique_mask);
     237                 :            :             }
     238                 :            :         }
     239                 :            :     }
     240                 :          1 : }
     241                 :            : 
     242                 :            : static void
     243                 :          1 : test_hash_main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
     244                 :            : {
     245                 :            :     /*
     246                 :            :      * The following tests check that all hashes computed with hash_function
     247                 :            :      * with one 1-bit (or no 1-bits) set within a X-bit word have different
     248                 :            :      * values in all N-bit consecutive comparisons.
     249                 :            :      *
     250                 :            :      *    test_function(hash_function, test_name, N)
     251                 :            :      *
     252                 :            :      * Given a random distribution, the probability of at least one collision
     253                 :            :      * in any set of N bits is approximately
     254                 :            :      *
     255                 :            :      *                      1 - (prob of no collisions)
     256                 :            :      *                          **(combination of all possible comparisons)
     257                 :            :      *                   == 1 - ((2**N - 1)/2**N)**C(X+1,2)
     258                 :            :      *                   == p
     259                 :            :      *
     260                 :            :      * There are (X-N) ways to pick N consecutive bits in a X-bit word, so if we
     261                 :            :      * assumed independence then the chance of having no collisions in any of
     262                 :            :      * those X-bit runs would be (1-p)**(X-N) == q.  If this q is very small
     263                 :            :      * and we can also find a relatively small 'magic number' N such that there
     264                 :            :      * is no collision in any comparison, then it means we have a pretty good
     265                 :            :      * hash function.
     266                 :            :      *
     267                 :            :      * The values of each parameters mentioned above for the tested hash
     268                 :            :      * functions are summarized as follow:
     269                 :            :      *
     270                 :            :      * hash_function       X      N        p             q
     271                 :            :      * -------------      ---    ---    -------       -------
     272                 :            :      *
     273                 :            :      * hash_words_cb       32     11     0.22          0.0044
     274                 :            :      * jhash_words_cb      32     11     0.22          0.0044
     275                 :            :      * hash_int_cb         32     12     0.12          0.0078
     276                 :            :      * hash_bytes128      128     19     0.0156        0.174
     277                 :            :      *
     278                 :            :      */
     279                 :          1 :     check_word_hash(hash_words_cb, "hash_words", 11);
     280                 :          1 :     check_word_hash(jhash_words_cb, "jhash_words", 11);
     281                 :          1 :     check_word_hash(hash_int_cb, "hash_int", 12);
     282                 :          1 :     check_hash_bytes128(hash_bytes128, "hash_bytes128", 19);
     283                 :            : 
     284                 :            :     /*
     285                 :            :      * The following tests check that all hashes computed with hash_function
     286                 :            :      * with one 1-bit (or no 1-bits) set within Y X-bit word have different
     287                 :            :      * values in their lowest N bits.
     288                 :            :      *
     289                 :            :      *    test_function(hash_function, test_name, N)
     290                 :            :      *
     291                 :            :      * Given a random distribution, the probability of at least one collision
     292                 :            :      * in any set of N bits is approximately
     293                 :            :      *
     294                 :            :      *                      1 - (prob of no collisions)
     295                 :            :      *                          **(combination of all possible comparisons)
     296                 :            :      *                   == 1 - ((2**N - 1)/2**N)**C(Y*X+1,2)
     297                 :            :      *                   == p
     298                 :            :      *
     299                 :            :      * If this p is not very small and we can also find a relatively small
     300                 :            :      * 'magic number' N such that there is no collision in any comparison,
     301                 :            :      * then it means we have a pretty good hash function.
     302                 :            :      *
     303                 :            :      * The values of each parameters mentioned above for the tested hash
     304                 :            :      * functions are summarized as follow:
     305                 :            :      *
     306                 :            :      * hash_function       Y      X      N        p
     307                 :            :      * -------------      ---    ---    ---    -------
     308                 :            :      *
     309                 :            :      * hash_words          3      32     12     0.68
     310                 :            :      * jhash_words         3      32     12     0.68
     311                 :            :      * hash_bytes128      16     128     23     0.22
     312                 :            :      *
     313                 :            :      */
     314                 :          1 :     check_3word_hash(hash_words, "hash_words");
     315                 :          1 :     check_3word_hash(jhash_words, "jhash_words");
     316                 :          1 :     check_256byte_hash(hash_bytes128, "hash_bytes128", 23);
     317                 :          1 : }
     318                 :            : 
     319                 :       1176 : OVSTEST_REGISTER("test-hash", test_hash_main);

Generated by: LCOV version 1.12