Path: blob/main/crypto/krb5/src/util/support/hashtab.c
34889 views
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */1/* util/support/hash.c - hash table implementation */2/*3* Copyright (C) 2018 by the Massachusetts Institute of Technology.4* All rights reserved.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9*10* * Redistributions of source code must retain the above copyright11* notice, this list of conditions and the following disclaimer.12*13* * Redistributions in binary form must reproduce the above copyright14* notice, this list of conditions and the following disclaimer in15* the documentation and/or other materials provided with the16* distribution.17*18* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS19* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT20* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS21* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE22* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,23* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES24* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR25* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)26* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,27* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)28* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED29* OF THE POSSIBILITY OF SUCH DAMAGE.30*/3132#include "k5-platform.h"33#include "k5-hashtab.h"34#include "k5-queue.h"3536struct entry {37const void *key;38size_t klen;39void *val;40K5_SLIST_ENTRY(entry) next;41};4243struct k5_hashtab {44uint64_t k0;45uint64_t k1;46size_t nbuckets;47size_t nentries;48K5_SLIST_HEAD(bucket_list, entry) *buckets;49};5051/* Return x rotated to the left by r bits. */52static inline uint64_t53rotl64(uint64_t x, int r)54{55return (x << r) | (x >> (64 - r));56}5758static inline void59sipround(uint64_t *v0, uint64_t *v1, uint64_t *v2, uint64_t *v3)60{61*v0 += *v1;62*v2 += *v3;63*v1 = rotl64(*v1, 13) ^ *v0;64*v3 = rotl64(*v3, 16) ^ *v2;65*v0 = rotl64(*v0, 32);66*v2 += *v1;67*v0 += *v3;68*v1 = rotl64(*v1, 17) ^ *v2;69*v3 = rotl64(*v3, 21) ^ *v0;70*v2 = rotl64(*v2, 32);71}7273/* SipHash-2-4 from https://131002.net/siphash/siphash.pdf (Jean-Philippe74* Aumasson and Daniel J. Bernstein) */75static uint64_t76siphash24(const uint8_t *data, size_t len, uint64_t k0, uint64_t k1)77{78uint64_t v0 = k0 ^ 0x736F6D6570736575;79uint64_t v1 = k1 ^ 0x646F72616E646F6D;80uint64_t v2 = k0 ^ 0x6C7967656E657261;81uint64_t v3 = k1 ^ 0x7465646279746573;82uint64_t mi;83const uint8_t *p, *end = data + (len - len % 8);84uint8_t last[8] = { 0 };8586/* Process each full 8-byte chunk of data. */87for (p = data; p < end; p += 8) {88mi = load_64_le(p);89v3 ^= mi;90sipround(&v0, &v1, &v2, &v3);91sipround(&v0, &v1, &v2, &v3);92v0 ^= mi;93}9495/* Process the last 0-7 bytes followed by the length mod 256. */96memcpy(last, end, len % 8);97last[7] = len & 0xFF;98mi = load_64_le(last);99v3 ^= mi;100sipround(&v0, &v1, &v2, &v3);101sipround(&v0, &v1, &v2, &v3);102v0 ^= mi;103104/* Finalize. */105v2 ^= 0xFF;106sipround(&v0, &v1, &v2, &v3);107sipround(&v0, &v1, &v2, &v3);108sipround(&v0, &v1, &v2, &v3);109sipround(&v0, &v1, &v2, &v3);110return v0 ^ v1 ^ v2 ^ v3;111}112113uint64_t114k5_siphash24(const uint8_t *data, size_t len,115const uint8_t seed[K5_HASH_SEED_LEN])116{117uint64_t k0 = load_64_le(seed), k1 = load_64_le(seed + 8);118119return siphash24(data, len, k0, k1);120}121122int123k5_hashtab_create(const uint8_t seed[K5_HASH_SEED_LEN], size_t initial_buckets,124struct k5_hashtab **ht_out)125{126struct k5_hashtab *ht;127128*ht_out = NULL;129130ht = malloc(sizeof(*ht));131if (ht == NULL)132return ENOMEM;133134if (seed != NULL) {135ht->k0 = load_64_le(seed);136ht->k1 = load_64_le(seed + 8);137} else {138ht->k0 = ht->k1 = 0;139}140ht->nbuckets = (initial_buckets > 0) ? initial_buckets : 64;141ht->nentries = 0;142ht->buckets = calloc(ht->nbuckets, sizeof(*ht->buckets));143if (ht->buckets == NULL) {144free(ht);145return ENOMEM;146}147148*ht_out = ht;149return 0;150}151152void153k5_hashtab_free(struct k5_hashtab *ht)154{155size_t i;156struct entry *ent;157158for (i = 0; i < ht->nbuckets; i++) {159while (!K5_SLIST_EMPTY(&ht->buckets[i])) {160ent = K5_SLIST_FIRST(&ht->buckets[i]);161K5_SLIST_REMOVE_HEAD(&ht->buckets[i], next);162free(ent);163}164}165free(ht->buckets);166free(ht);167}168169static int170resize_table(struct k5_hashtab *ht)171{172size_t i, j, newsize = ht->nbuckets * 2;173struct bucket_list *newbuckets;174struct entry *ent;175176newbuckets = calloc(newsize, sizeof(*newbuckets));177if (newbuckets == NULL)178return ENOMEM;179180/* Rehash all the entries into the new buckets. */181for (i = 0; i < ht->nbuckets; i++) {182while (!K5_SLIST_EMPTY(&ht->buckets[i])) {183ent = K5_SLIST_FIRST(&ht->buckets[i]);184j = siphash24(ent->key, ent->klen, ht->k0, ht->k1) % newsize;185K5_SLIST_REMOVE_HEAD(&ht->buckets[i], next);186K5_SLIST_INSERT_HEAD(&newbuckets[j], ent, next);187}188}189190free(ht->buckets);191ht->buckets = newbuckets;192ht->nbuckets = newsize;193return 0;194}195196int197k5_hashtab_add(struct k5_hashtab *ht, const void *key, size_t klen, void *val)198{199size_t i;200struct entry *ent;201202if (ht->nentries == ht->nbuckets) {203if (resize_table(ht) != 0)204return ENOMEM;205}206207ent = malloc(sizeof(*ent));208if (ent == NULL)209return ENOMEM;210ent->key = key;211ent->klen = klen;212ent->val = val;213214i = siphash24(key, klen, ht->k0, ht->k1) % ht->nbuckets;215K5_SLIST_INSERT_HEAD(&ht->buckets[i], ent, next);216217ht->nentries++;218return 0;219}220221int222k5_hashtab_remove(struct k5_hashtab *ht, const void *key, size_t klen)223{224size_t i;225struct entry *ent;226227i = siphash24(key, klen, ht->k0, ht->k1) % ht->nbuckets;228K5_SLIST_FOREACH(ent, &ht->buckets[i], next) {229if (ent->klen == klen && memcmp(ent->key, key, klen) == 0) {230K5_SLIST_REMOVE(&ht->buckets[i], ent, entry, next);231free(ent);232ht->nentries--;233return 1;234}235}236return 0;237}238239void *240k5_hashtab_get(struct k5_hashtab *ht, const void *key, size_t klen)241{242size_t i;243struct entry *ent;244245i = siphash24(key, klen, ht->k0, ht->k1) % ht->nbuckets;246K5_SLIST_FOREACH(ent, &ht->buckets[i], next) {247if (ent->klen == klen && memcmp(ent->key, key, klen) == 0)248return ent->val;249}250return NULL;251}252253254