053d093524
Type: improvement Signed-off-by: Dave Barach <dave@barachs.net> Change-Id: Id28299a188feefa1899d835fd499f018af95d81b
860 lines
20 KiB
C
860 lines
20 KiB
C
/*
|
|
* Copyright (c) 2015 Cisco and/or its affiliates.
|
|
* 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.
|
|
*/
|
|
/*
|
|
Copyright (c) 2006 Eliot Dresselhaus
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining
|
|
a copy of this software and associated documentation files (the
|
|
"Software"), to deal in the Software without restriction, including
|
|
without limitation the rights to use, copy, modify, merge, publish,
|
|
distribute, sublicense, and/or sell copies of the Software, and to
|
|
permit persons to whom the Software is furnished to do so, subject to
|
|
the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be
|
|
included in all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
#include <vppinfra/qhash.h>
|
|
|
|
#define QHASH_ALL_VALID ((1 << QHASH_KEYS_PER_BUCKET) - 1)
|
|
|
|
void *
|
|
_qhash_resize (void *v, uword length, uword elt_bytes)
|
|
{
|
|
qhash_t *h;
|
|
uword l;
|
|
|
|
l = clib_max (max_log2 (length), 2 + QHASH_LOG2_KEYS_PER_BUCKET);
|
|
|
|
/* Round up if less than 1/2 full. */
|
|
l += ((f64) length / (f64) (1 << l)) < .5;
|
|
|
|
v = _vec_resize (0, 1 << l, elt_bytes << l, sizeof (h[0]),
|
|
/* align */ sizeof (uword));
|
|
|
|
h = qhash_header (v);
|
|
h->n_elts = 0;
|
|
h->log2_hash_size = l;
|
|
h->hash_keys =
|
|
clib_mem_alloc_aligned_no_fail (sizeof (h->hash_keys[0]) << l,
|
|
CLIB_CACHE_LINE_BYTES);
|
|
vec_resize (h->hash_key_valid_bitmap,
|
|
1 << (l - QHASH_LOG2_KEYS_PER_BUCKET));
|
|
clib_memset (v, ~0, elt_bytes << l);
|
|
|
|
return v;
|
|
}
|
|
|
|
static u8 min_log2_table[256];
|
|
|
|
static inline uword
|
|
qhash_min_log2 (uword x)
|
|
{
|
|
ASSERT (is_pow2 (x));
|
|
ASSERT (x < 256);
|
|
return min_log2_table[x];
|
|
}
|
|
|
|
static void
|
|
qhash_min_log2_init ()
|
|
{
|
|
int i;
|
|
for (i = 0; i < 256; i++)
|
|
min_log2_table[i] = min_log2 (i);
|
|
}
|
|
|
|
always_inline uword
|
|
qhash_get_valid_elt_mask (qhash_t * h, uword i)
|
|
{
|
|
return h->hash_key_valid_bitmap[i / QHASH_KEYS_PER_BUCKET];
|
|
}
|
|
|
|
always_inline void
|
|
qhash_set_valid_elt_mask (qhash_t * h, uword i, uword mask)
|
|
{
|
|
h->hash_key_valid_bitmap[i / QHASH_KEYS_PER_BUCKET] = mask;
|
|
}
|
|
|
|
always_inline uword
|
|
qhash_search_bucket (uword * hash_keys, uword search_key, uword m)
|
|
{
|
|
uword t;
|
|
#define _(i) ((hash_keys[i] == search_key) << i)
|
|
t = (_(0) | _(1) | _(2) | _(3));
|
|
if (QHASH_KEYS_PER_BUCKET > 4)
|
|
t |= (_(4) | _(5) | _(6) | _(7));
|
|
if (QHASH_KEYS_PER_BUCKET > 8)
|
|
t |= (_(8) | _(9) | _(10) | _(11) | _(12) | _(13) | _(14) | _(15));
|
|
#undef _
|
|
return m & t;
|
|
}
|
|
|
|
/* Lookup multiple keys in the same hash table. */
|
|
void
|
|
qhash_get_multiple (void *v,
|
|
uword * search_keys,
|
|
uword n_search_keys, u32 * result_indices)
|
|
{
|
|
qhash_t *h = qhash_header (v);
|
|
uword *k, *hash_keys;
|
|
uword n_left, bucket_mask;
|
|
u32 *r;
|
|
|
|
if (!v)
|
|
{
|
|
clib_memset (result_indices, ~0,
|
|
sizeof (result_indices[0]) * n_search_keys);
|
|
return;
|
|
}
|
|
|
|
bucket_mask = pow2_mask (h->log2_hash_size) & ~(QHASH_KEYS_PER_BUCKET - 1);
|
|
|
|
k = search_keys;
|
|
n_left = n_search_keys;
|
|
hash_keys = h->hash_keys;
|
|
r = result_indices;
|
|
|
|
while (n_left >= 2)
|
|
{
|
|
u32 a0, b0, c0, bi0, valid0, match0;
|
|
u32 a1, b1, c1, bi1, valid1, match1;
|
|
uword k0, k1, *h0, *h1;
|
|
|
|
k0 = k[0];
|
|
k1 = k[1];
|
|
n_left -= 2;
|
|
k += 2;
|
|
|
|
a0 = a1 = h->hash_seeds[0];
|
|
b0 = b1 = h->hash_seeds[1];
|
|
c0 = c1 = h->hash_seeds[2];
|
|
a0 ^= k0;
|
|
a1 ^= k1;
|
|
#if uword_bits == 64
|
|
b0 ^= k0 >> 32;
|
|
b1 ^= k1 >> 32;
|
|
#endif
|
|
|
|
hash_mix32_step_1 (a0, b0, c0);
|
|
hash_mix32_step_1 (a1, b1, c1);
|
|
hash_mix32_step_2 (a0, b0, c0);
|
|
hash_mix32_step_2 (a1, b1, c1);
|
|
hash_mix32_step_3 (a0, b0, c0);
|
|
hash_mix32_step_3 (a1, b1, c1);
|
|
|
|
bi0 = c0 & bucket_mask;
|
|
bi1 = c1 & bucket_mask;
|
|
|
|
h0 = hash_keys + bi0;
|
|
h1 = hash_keys + bi1;
|
|
|
|
/* Search two buckets. */
|
|
valid0 = qhash_get_valid_elt_mask (h, bi0);
|
|
valid1 = qhash_get_valid_elt_mask (h, bi1);
|
|
|
|
match0 = qhash_search_bucket (h0, k0, valid0);
|
|
match1 = qhash_search_bucket (h1, k1, valid1);
|
|
|
|
bi0 += qhash_min_log2 (match0);
|
|
bi1 += qhash_min_log2 (match1);
|
|
|
|
r[0] = match0 ? bi0 : ~0;
|
|
r[1] = match1 ? bi1 : ~0;
|
|
r += 2;
|
|
|
|
/* Full buckets trigger search of overflow hash. */
|
|
if (PREDICT_FALSE (!match0 && valid0 == QHASH_ALL_VALID))
|
|
{
|
|
uword *p = hash_get (h->overflow_hash, k0);
|
|
r[-2] = p ? p[0] : ~0;
|
|
}
|
|
|
|
/* Full buckets trigger search of overflow hash. */
|
|
if (PREDICT_FALSE (!match1 && valid1 == QHASH_ALL_VALID))
|
|
{
|
|
uword *p = hash_get (h->overflow_hash, k1);
|
|
r[-1] = p ? p[0] : ~0;
|
|
}
|
|
}
|
|
|
|
while (n_left >= 1)
|
|
{
|
|
u32 a0, b0, c0, bi0, valid0, match0;
|
|
uword k0, *h0;
|
|
|
|
k0 = k[0];
|
|
n_left -= 1;
|
|
k += 1;
|
|
|
|
a0 = h->hash_seeds[0];
|
|
b0 = h->hash_seeds[1];
|
|
c0 = h->hash_seeds[2];
|
|
a0 ^= k0;
|
|
#if uword_bits == 64
|
|
b0 ^= k0 >> 32;
|
|
#endif
|
|
|
|
hash_mix32 (a0, b0, c0);
|
|
|
|
bi0 = c0 & bucket_mask;
|
|
|
|
h0 = hash_keys + bi0;
|
|
|
|
/* Search one bucket. */
|
|
valid0 = qhash_get_valid_elt_mask (h, bi0);
|
|
match0 = qhash_search_bucket (h0, k0, valid0);
|
|
|
|
bi0 += qhash_min_log2 (match0);
|
|
|
|
r[0] = match0 ? bi0 : ~0;
|
|
r += 1;
|
|
|
|
/* Full buckets trigger search of overflow hash. */
|
|
if (PREDICT_FALSE (!match0 && valid0 == QHASH_ALL_VALID))
|
|
{
|
|
uword *p = hash_get (h->overflow_hash, k0);
|
|
r[-1] = p ? p[0] : ~0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Lookup multiple keys in the same hash table.
|
|
Returns index of first matching key. */
|
|
u32
|
|
qhash_get_first_match (void *v,
|
|
uword * search_keys,
|
|
uword n_search_keys, uword * matching_key)
|
|
{
|
|
qhash_t *h = qhash_header (v);
|
|
uword *k, *hash_keys;
|
|
uword n_left, match_mask, bucket_mask;
|
|
|
|
if (!v)
|
|
return ~0;
|
|
|
|
match_mask = 0;
|
|
bucket_mask = pow2_mask (h->log2_hash_size) & ~(QHASH_KEYS_PER_BUCKET - 1);
|
|
|
|
k = search_keys;
|
|
n_left = n_search_keys;
|
|
hash_keys = h->hash_keys;
|
|
while (n_left >= 2)
|
|
{
|
|
u32 a0, b0, c0, bi0, valid0;
|
|
u32 a1, b1, c1, bi1, valid1;
|
|
uword k0, k1, *h0, *h1;
|
|
|
|
k0 = k[0];
|
|
k1 = k[1];
|
|
n_left -= 2;
|
|
k += 2;
|
|
|
|
a0 = a1 = h->hash_seeds[0];
|
|
b0 = b1 = h->hash_seeds[1];
|
|
c0 = c1 = h->hash_seeds[2];
|
|
a0 ^= k0;
|
|
a1 ^= k1;
|
|
#if uword_bits == 64
|
|
b0 ^= k0 >> 32;
|
|
b1 ^= k1 >> 32;
|
|
#endif
|
|
|
|
hash_mix32_step_1 (a0, b0, c0);
|
|
hash_mix32_step_1 (a1, b1, c1);
|
|
hash_mix32_step_2 (a0, b0, c0);
|
|
hash_mix32_step_2 (a1, b1, c1);
|
|
hash_mix32_step_3 (a0, b0, c0);
|
|
hash_mix32_step_3 (a1, b1, c1);
|
|
|
|
bi0 = c0 & bucket_mask;
|
|
bi1 = c1 & bucket_mask;
|
|
|
|
h0 = hash_keys + bi0;
|
|
h1 = hash_keys + bi1;
|
|
|
|
/* Search two buckets. */
|
|
valid0 = qhash_get_valid_elt_mask (h, bi0);
|
|
valid1 = qhash_get_valid_elt_mask (h, bi1);
|
|
match_mask = qhash_search_bucket (h0, k0, valid0);
|
|
match_mask |= (qhash_search_bucket (h1, k1, valid1)
|
|
<< QHASH_KEYS_PER_BUCKET);
|
|
if (match_mask)
|
|
{
|
|
uword bi, is_match1;
|
|
|
|
bi = qhash_min_log2 (match_mask);
|
|
is_match1 = bi >= QHASH_KEYS_PER_BUCKET;
|
|
|
|
bi += ((is_match1 ? bi1 : bi0)
|
|
- (is_match1 << QHASH_LOG2_KEYS_PER_BUCKET));
|
|
*matching_key = (k - 2 - search_keys) + is_match1;
|
|
return bi;
|
|
}
|
|
|
|
/* Full buckets trigger search of overflow hash. */
|
|
if (PREDICT_FALSE (valid0 == QHASH_ALL_VALID
|
|
|| valid1 == QHASH_ALL_VALID))
|
|
{
|
|
uword *p = 0;
|
|
uword ki = k - 2 - search_keys;
|
|
|
|
if (valid0 == QHASH_ALL_VALID)
|
|
p = hash_get (h->overflow_hash, k0);
|
|
|
|
if (!p && valid1 == QHASH_ALL_VALID)
|
|
{
|
|
p = hash_get (h->overflow_hash, k1);
|
|
ki++;
|
|
}
|
|
|
|
if (p)
|
|
{
|
|
*matching_key = ki;
|
|
return p[0];
|
|
}
|
|
}
|
|
}
|
|
|
|
while (n_left >= 1)
|
|
{
|
|
u32 a0, b0, c0, bi0, valid0;
|
|
uword k0, *h0;
|
|
|
|
k0 = k[0];
|
|
n_left -= 1;
|
|
k += 1;
|
|
|
|
a0 = h->hash_seeds[0];
|
|
b0 = h->hash_seeds[1];
|
|
c0 = h->hash_seeds[2];
|
|
a0 ^= k0;
|
|
#if uword_bits == 64
|
|
b0 ^= k0 >> 32;
|
|
#endif
|
|
|
|
hash_mix32 (a0, b0, c0);
|
|
|
|
bi0 = c0 & bucket_mask;
|
|
|
|
h0 = hash_keys + bi0;
|
|
|
|
/* Search one bucket. */
|
|
valid0 = qhash_get_valid_elt_mask (h, bi0);
|
|
match_mask = qhash_search_bucket (h0, k0, valid0);
|
|
if (match_mask)
|
|
{
|
|
uword bi;
|
|
bi = bi0 + qhash_min_log2 (match_mask);
|
|
*matching_key = (k - 1 - search_keys);
|
|
return bi;
|
|
}
|
|
|
|
/* Full buckets trigger search of overflow hash. */
|
|
if (PREDICT_FALSE (valid0 == QHASH_ALL_VALID))
|
|
{
|
|
uword *p = hash_get (h->overflow_hash, k0);
|
|
if (p)
|
|
{
|
|
*matching_key = (k - 1 - search_keys);
|
|
return p[0];
|
|
}
|
|
}
|
|
}
|
|
|
|
return ~0;
|
|
}
|
|
|
|
static void *
|
|
qhash_set_overflow (void *v, uword elt_bytes,
|
|
uword key, uword bi, uword * n_elts, u32 * result)
|
|
{
|
|
qhash_t *h = qhash_header (v);
|
|
uword *p = hash_get (h->overflow_hash, key);
|
|
uword i;
|
|
|
|
bi /= QHASH_KEYS_PER_BUCKET;
|
|
|
|
if (p)
|
|
i = p[0];
|
|
else
|
|
{
|
|
uword l = vec_len (h->overflow_free_indices);
|
|
if (l > 0)
|
|
{
|
|
i = h->overflow_free_indices[l - 1];
|
|
_vec_len (h->overflow_free_indices) = l - 1;
|
|
}
|
|
else
|
|
i = (1 << h->log2_hash_size) + hash_elts (h->overflow_hash);
|
|
hash_set (h->overflow_hash, key, i);
|
|
vec_validate (h->overflow_counts, bi);
|
|
h->overflow_counts[bi] += 1;
|
|
*n_elts += 1;
|
|
|
|
l = vec_len (v);
|
|
if (i >= l)
|
|
{
|
|
uword dl = round_pow2 (1 + i - l, 8);
|
|
v = _vec_resize (v, dl, (l + dl) * elt_bytes, sizeof (h[0]),
|
|
/* align */ sizeof (uword));
|
|
clib_memset (v + l * elt_bytes, ~0, dl * elt_bytes);
|
|
}
|
|
}
|
|
|
|
*result = i;
|
|
|
|
return v;
|
|
}
|
|
|
|
static uword
|
|
qhash_unset_overflow (void *v, uword key, uword bi, uword * n_elts)
|
|
{
|
|
qhash_t *h = qhash_header (v);
|
|
uword *p = hash_get (h->overflow_hash, key);
|
|
uword result;
|
|
|
|
bi /= QHASH_KEYS_PER_BUCKET;
|
|
|
|
if (p)
|
|
{
|
|
result = p[0];
|
|
hash_unset (h->overflow_hash, key);
|
|
ASSERT (bi < vec_len (h->overflow_counts));
|
|
ASSERT (h->overflow_counts[bi] > 0);
|
|
ASSERT (*n_elts > 0);
|
|
vec_add1 (h->overflow_free_indices, result);
|
|
h->overflow_counts[bi] -= 1;
|
|
*n_elts -= 1;
|
|
}
|
|
else
|
|
result = ~0;
|
|
|
|
return result;
|
|
}
|
|
|
|
always_inline uword
|
|
qhash_find_free (uword i, uword valid_mask)
|
|
{
|
|
return first_set (~valid_mask & pow2_mask (QHASH_KEYS_PER_BUCKET));
|
|
}
|
|
|
|
void *
|
|
_qhash_set_multiple (void *v,
|
|
uword elt_bytes,
|
|
uword * search_keys,
|
|
uword n_search_keys, u32 * result_indices)
|
|
{
|
|
qhash_t *h = qhash_header (v);
|
|
uword *k, *hash_keys;
|
|
uword n_left, n_elts, bucket_mask;
|
|
u32 *r;
|
|
|
|
if (vec_len (v) < n_search_keys)
|
|
v = _qhash_resize (v, n_search_keys, elt_bytes);
|
|
|
|
if (qhash_min_log2 (2) != 1)
|
|
{
|
|
qhash_min_log2_init ();
|
|
ASSERT (qhash_min_log2 (2) == 1);
|
|
}
|
|
|
|
ASSERT (v != 0);
|
|
|
|
bucket_mask = pow2_mask (h->log2_hash_size) & ~(QHASH_KEYS_PER_BUCKET - 1);
|
|
|
|
hash_keys = h->hash_keys;
|
|
k = search_keys;
|
|
r = result_indices;
|
|
n_left = n_search_keys;
|
|
n_elts = h->n_elts;
|
|
|
|
while (n_left >= 2)
|
|
{
|
|
u32 a0, b0, c0, bi0, match0, valid0, free0;
|
|
u32 a1, b1, c1, bi1, match1, valid1, free1;
|
|
uword k0, *h0;
|
|
uword k1, *h1;
|
|
|
|
k0 = k[0];
|
|
k1 = k[1];
|
|
|
|
/* Keys must be unique. */
|
|
ASSERT (k0 != k1);
|
|
|
|
n_left -= 2;
|
|
k += 2;
|
|
|
|
a0 = a1 = h->hash_seeds[0];
|
|
b0 = b1 = h->hash_seeds[1];
|
|
c0 = c1 = h->hash_seeds[2];
|
|
a0 ^= k0;
|
|
a1 ^= k1;
|
|
#if uword_bits == 64
|
|
b0 ^= k0 >> 32;
|
|
b1 ^= k1 >> 32;
|
|
#endif
|
|
|
|
hash_mix32_step_1 (a0, b0, c0);
|
|
hash_mix32_step_1 (a1, b1, c1);
|
|
hash_mix32_step_2 (a0, b0, c0);
|
|
hash_mix32_step_2 (a1, b1, c1);
|
|
hash_mix32_step_3 (a0, b0, c0);
|
|
hash_mix32_step_3 (a1, b1, c1);
|
|
|
|
bi0 = c0 & bucket_mask;
|
|
bi1 = c1 & bucket_mask;
|
|
|
|
h0 = hash_keys + bi0;
|
|
h1 = hash_keys + bi1;
|
|
|
|
/* Search two buckets. */
|
|
valid0 = qhash_get_valid_elt_mask (h, bi0);
|
|
valid1 = qhash_get_valid_elt_mask (h, bi1);
|
|
|
|
match0 = qhash_search_bucket (h0, k0, valid0);
|
|
match1 = qhash_search_bucket (h1, k1, valid1);
|
|
|
|
/* Find first free element starting at hash offset into bucket. */
|
|
free0 = qhash_find_free (c0 & (QHASH_KEYS_PER_BUCKET - 1), valid0);
|
|
|
|
valid1 = valid1 | (bi0 == bi1 ? free0 : 0);
|
|
free1 = qhash_find_free (c1 & (QHASH_KEYS_PER_BUCKET - 1), valid1);
|
|
|
|
n_elts += (match0 == 0) + (match1 == 0);
|
|
|
|
match0 = match0 ? match0 : free0;
|
|
match1 = match1 ? match1 : free1;
|
|
|
|
valid0 |= match0;
|
|
valid1 |= match1;
|
|
|
|
h0 += qhash_min_log2 (match0);
|
|
h1 += qhash_min_log2 (match1);
|
|
|
|
if (PREDICT_FALSE (!match0 || !match1))
|
|
goto slow_path2;
|
|
|
|
h0[0] = k0;
|
|
h1[0] = k1;
|
|
r[0] = h0 - hash_keys;
|
|
r[1] = h1 - hash_keys;
|
|
r += 2;
|
|
qhash_set_valid_elt_mask (h, bi0, valid0);
|
|
qhash_set_valid_elt_mask (h, bi1, valid1);
|
|
continue;
|
|
|
|
slow_path2:
|
|
if (!match0)
|
|
{
|
|
n_elts -= 1;
|
|
v = qhash_set_overflow (v, elt_bytes, k0, bi0, &n_elts, &r[0]);
|
|
}
|
|
else
|
|
{
|
|
h0[0] = k0;
|
|
r[0] = h0 - hash_keys;
|
|
qhash_set_valid_elt_mask (h, bi0, valid0);
|
|
}
|
|
if (!match1)
|
|
{
|
|
n_elts -= 1;
|
|
v = qhash_set_overflow (v, elt_bytes, k1, bi1, &n_elts, &r[1]);
|
|
}
|
|
else
|
|
{
|
|
h1[0] = k1;
|
|
r[1] = h1 - hash_keys;
|
|
qhash_set_valid_elt_mask (h, bi1, valid1);
|
|
}
|
|
r += 2;
|
|
}
|
|
|
|
while (n_left >= 1)
|
|
{
|
|
u32 a0, b0, c0, bi0, match0, valid0, free0;
|
|
uword k0, *h0;
|
|
|
|
k0 = k[0];
|
|
n_left -= 1;
|
|
k += 1;
|
|
|
|
a0 = h->hash_seeds[0];
|
|
b0 = h->hash_seeds[1];
|
|
c0 = h->hash_seeds[2];
|
|
a0 ^= k0;
|
|
#if uword_bits == 64
|
|
b0 ^= k0 >> 32;
|
|
#endif
|
|
|
|
hash_mix32 (a0, b0, c0);
|
|
|
|
bi0 = c0 & bucket_mask;
|
|
|
|
h0 = hash_keys + bi0;
|
|
|
|
valid0 = qhash_get_valid_elt_mask (h, bi0);
|
|
|
|
/* Find first free element starting at hash offset into bucket. */
|
|
free0 = qhash_find_free (c0 & (QHASH_KEYS_PER_BUCKET - 1), valid0);
|
|
|
|
match0 = qhash_search_bucket (h0, k0, valid0);
|
|
|
|
n_elts += (match0 == 0);
|
|
|
|
match0 = match0 ? match0 : free0;
|
|
|
|
valid0 |= match0;
|
|
|
|
h0 += qhash_min_log2 (match0);
|
|
|
|
if (PREDICT_FALSE (!match0))
|
|
goto slow_path1;
|
|
|
|
h0[0] = k0;
|
|
r[0] = h0 - hash_keys;
|
|
r += 1;
|
|
qhash_set_valid_elt_mask (h, bi0, valid0);
|
|
continue;
|
|
|
|
slow_path1:
|
|
n_elts -= 1;
|
|
v = qhash_set_overflow (v, elt_bytes, k0, bi0, &n_elts, &r[0]);
|
|
r += 1;
|
|
}
|
|
|
|
h = qhash_header (v);
|
|
h->n_elts = n_elts;
|
|
|
|
return v;
|
|
}
|
|
|
|
static uword
|
|
unset_slow_path (void *v, uword elt_bytes,
|
|
uword k0, uword bi0, uword valid0, uword match0,
|
|
uword * n_elts)
|
|
{
|
|
qhash_t *h = qhash_header (v);
|
|
uword i, j = 0, k, l, t = ~0;
|
|
hash_pair_t *p, *found;
|
|
|
|
if (!match0)
|
|
{
|
|
if (valid0 == QHASH_ALL_VALID)
|
|
t = qhash_unset_overflow (v, k0, bi0, n_elts);
|
|
return t;
|
|
}
|
|
|
|
i = bi0 / QHASH_KEYS_PER_BUCKET;
|
|
t = bi0 + qhash_min_log2 (match0);
|
|
|
|
if (valid0 == QHASH_ALL_VALID
|
|
&& i < vec_len (h->overflow_counts) && h->overflow_counts[i] > 0)
|
|
{
|
|
found = 0;
|
|
/* *INDENT-OFF* */
|
|
hash_foreach_pair (p, h->overflow_hash, ({
|
|
j = qhash_hash_mix (h, p->key) / QHASH_KEYS_PER_BUCKET;
|
|
if (j == i)
|
|
{
|
|
found = p;
|
|
break;
|
|
}
|
|
}));
|
|
/* *INDENT-ON* */
|
|
ASSERT (found != 0);
|
|
ASSERT (j == i);
|
|
|
|
l = found->value[0];
|
|
k = found->key;
|
|
hash_unset3 (h->overflow_hash, k, &j);
|
|
vec_add1 (h->overflow_free_indices, j);
|
|
h->overflow_counts[i] -= 1;
|
|
|
|
qhash_set_valid_elt_mask (h, bi0, valid0);
|
|
|
|
h->hash_keys[t] = k;
|
|
clib_memswap (v + t * elt_bytes, v + l * elt_bytes, elt_bytes);
|
|
t = l;
|
|
}
|
|
else
|
|
qhash_set_valid_elt_mask (h, bi0, valid0 ^ match0);
|
|
|
|
return t;
|
|
}
|
|
|
|
void
|
|
_qhash_unset_multiple (void *v,
|
|
uword elt_bytes,
|
|
uword * search_keys,
|
|
uword n_search_keys, u32 * result_indices)
|
|
{
|
|
qhash_t *h = qhash_header (v);
|
|
uword *k, *hash_keys;
|
|
uword n_left, n_elts, bucket_mask;
|
|
u32 *r;
|
|
|
|
if (!v)
|
|
{
|
|
uword i;
|
|
for (i = 0; i < n_search_keys; i++)
|
|
result_indices[i] = ~0;
|
|
}
|
|
|
|
bucket_mask = pow2_mask (h->log2_hash_size) & ~(QHASH_KEYS_PER_BUCKET - 1);
|
|
|
|
hash_keys = h->hash_keys;
|
|
k = search_keys;
|
|
r = result_indices;
|
|
n_left = n_search_keys;
|
|
n_elts = h->n_elts;
|
|
|
|
while (n_left >= 2)
|
|
{
|
|
u32 a0, b0, c0, bi0, match0, valid0;
|
|
u32 a1, b1, c1, bi1, match1, valid1;
|
|
uword k0, *h0;
|
|
uword k1, *h1;
|
|
|
|
k0 = k[0];
|
|
k1 = k[1];
|
|
|
|
/* Keys must be unique. */
|
|
ASSERT (k0 != k1);
|
|
|
|
n_left -= 2;
|
|
k += 2;
|
|
|
|
a0 = a1 = h->hash_seeds[0];
|
|
b0 = b1 = h->hash_seeds[1];
|
|
c0 = c1 = h->hash_seeds[2];
|
|
a0 ^= k0;
|
|
a1 ^= k1;
|
|
#if uword_bits == 64
|
|
b0 ^= k0 >> 32;
|
|
b1 ^= k1 >> 32;
|
|
#endif
|
|
|
|
hash_mix32_step_1 (a0, b0, c0);
|
|
hash_mix32_step_1 (a1, b1, c1);
|
|
hash_mix32_step_2 (a0, b0, c0);
|
|
hash_mix32_step_2 (a1, b1, c1);
|
|
hash_mix32_step_3 (a0, b0, c0);
|
|
hash_mix32_step_3 (a1, b1, c1);
|
|
|
|
bi0 = c0 & bucket_mask;
|
|
bi1 = c1 & bucket_mask;
|
|
|
|
h0 = hash_keys + bi0;
|
|
h1 = hash_keys + bi1;
|
|
|
|
/* Search two buckets. */
|
|
valid0 = qhash_get_valid_elt_mask (h, bi0);
|
|
valid1 = qhash_get_valid_elt_mask (h, bi1);
|
|
|
|
match0 = qhash_search_bucket (h0, k0, valid0);
|
|
match1 = qhash_search_bucket (h1, k1, valid1);
|
|
|
|
n_elts -= (match0 != 0) + (match1 != 0);
|
|
|
|
if (PREDICT_FALSE (valid0 == QHASH_ALL_VALID
|
|
|| valid1 == QHASH_ALL_VALID))
|
|
goto slow_path2;
|
|
|
|
valid0 ^= match0;
|
|
qhash_set_valid_elt_mask (h, bi0, valid0);
|
|
|
|
valid1 = bi0 == bi1 ? valid0 : valid1;
|
|
valid1 ^= match1;
|
|
|
|
qhash_set_valid_elt_mask (h, bi1, valid1);
|
|
|
|
r[0] = match0 ? bi0 + qhash_min_log2 (match0) : ~0;
|
|
r[1] = match1 ? bi1 + qhash_min_log2 (match1) : ~0;
|
|
r += 2;
|
|
continue;
|
|
|
|
slow_path2:
|
|
r[0] = unset_slow_path (v, elt_bytes, k0, bi0, valid0, match0, &n_elts);
|
|
if (bi0 == bi1)
|
|
{
|
|
/* Search again in same bucket to test new overflow element. */
|
|
valid1 = qhash_get_valid_elt_mask (h, bi0);
|
|
if (!match1)
|
|
{
|
|
match1 = qhash_search_bucket (h1, k1, valid1);
|
|
n_elts -= (match1 != 0);
|
|
}
|
|
}
|
|
r[1] = unset_slow_path (v, elt_bytes, k1, bi1, valid1, match1, &n_elts);
|
|
r += 2;
|
|
}
|
|
|
|
while (n_left >= 1)
|
|
{
|
|
u32 a0, b0, c0, bi0, match0, valid0;
|
|
uword k0, *h0;
|
|
|
|
k0 = k[0];
|
|
n_left -= 1;
|
|
k += 1;
|
|
|
|
a0 = h->hash_seeds[0];
|
|
b0 = h->hash_seeds[1];
|
|
c0 = h->hash_seeds[2];
|
|
a0 ^= k0;
|
|
#if uword_bits == 64
|
|
b0 ^= k0 >> 32;
|
|
#endif
|
|
|
|
hash_mix32 (a0, b0, c0);
|
|
|
|
bi0 = c0 & bucket_mask;
|
|
|
|
h0 = hash_keys + bi0;
|
|
|
|
valid0 = qhash_get_valid_elt_mask (h, bi0);
|
|
|
|
match0 = qhash_search_bucket (h0, k0, valid0);
|
|
n_elts -= (match0 != 0);
|
|
qhash_set_valid_elt_mask (h, bi0, valid0 ^ match0);
|
|
|
|
r[0] = match0 ? bi0 + qhash_min_log2 (match0) : ~0;
|
|
r += 1;
|
|
|
|
if (PREDICT_FALSE (valid0 == QHASH_ALL_VALID))
|
|
r[-1] = unset_slow_path (v, elt_bytes, k0, bi0, valid0, match0,
|
|
&n_elts);
|
|
}
|
|
|
|
h->n_elts = n_elts;
|
|
}
|
|
|
|
/*
|
|
* fd.io coding-style-patch-verification: ON
|
|
*
|
|
* Local Variables:
|
|
* eval: (c-set-style "gnu")
|
|
* End:
|
|
*/
|