diff --git a/bootstrap.conf b/bootstrap.conf index 04c76450fa..03da267cad 100644 --- a/bootstrap.conf +++ b/bootstrap.conf @@ -23,6 +23,7 @@ accept areadlink base64 bind +bitrotate byteswap c-ctype c-strcase diff --git a/src/Makefile.am b/src/Makefile.am index eacc741e0e..22fd58e0cc 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -90,6 +90,7 @@ UTIL_SOURCES = \ util/xml.c util/xml.h \ util/virterror.c util/virterror_internal.h \ util/virhash.c util/virhash.h \ + util/virhashcode.c util/virhashcode.h \ util/virkeycode.c util/virkeycode.h \ util/virkeymaps.h \ util/virnetdev.h util/virnetdev.c \ diff --git a/src/util/cgroup.c b/src/util/cgroup.c index e1a90b2a9a..3f092ab1a4 100644 --- a/src/util/cgroup.c +++ b/src/util/cgroup.c @@ -33,6 +33,7 @@ #include "logging.h" #include "virfile.h" #include "virhash.h" +#include "virhashcode.h" #define CGROUP_MAX_VAL 512 @@ -1636,9 +1637,10 @@ cleanup: } -static uint32_t virCgroupPidCode(const void *name) +static uint32_t virCgroupPidCode(const void *name, uint32_t seed) { - return (uint32_t)(intptr_t)name; + unsigned long pid = (unsigned long)(intptr_t)name; + return virHashCodeGen(&pid, sizeof(pid), seed); } static bool virCgroupPidEqual(const void *namea, const void *nameb) { diff --git a/src/util/virhash.c b/src/util/virhash.c index 238a6fe73a..efaa286b35 100644 --- a/src/util/virhash.c +++ b/src/util/virhash.c @@ -28,6 +28,8 @@ #include "virhash.h" #include "memory.h" #include "logging.h" +#include "virhashcode.h" +#include "virrandom.h" #define VIR_FROM_THIS VIR_FROM_NONE @@ -57,6 +59,7 @@ struct _virHashEntry { */ struct _virHashTable { virHashEntryPtr *table; + uint32_t seed; size_t size; size_t nbElems; /* True iff we are iterating over hash entries. */ @@ -70,20 +73,9 @@ struct _virHashTable { virHashKeyFree keyFree; }; -static uint32_t virHashStrCode(const void *name) +static uint32_t virHashStrCode(const void *name, uint32_t seed) { - const char *str = name; - uint32_t value = 0L; - char ch; - - if (str != NULL) { - value += 30 * (*str); - while ((ch = *str++) != 0) { - value = - value ^ ((value << 5) + (value >> 3) + (uint32_t) ch); - } - } - return value; + return virHashCodeGen(name, strlen(name), seed); } static bool virHashStrEqual(const void *namea, const void *nameb) @@ -105,7 +97,7 @@ static void virHashStrFree(void *name) static size_t virHashComputeKey(virHashTablePtr table, const void *name) { - uint32_t value = table->keyCode(name); + uint32_t value = table->keyCode(name, table->seed); return (value % table->size); } @@ -139,6 +131,7 @@ virHashTablePtr virHashCreateFull(ssize_t size, return NULL; } + table->seed = virRandomBits(32); table->size = size; table->nbElems = 0; table->dataFree = dataFree; diff --git a/src/util/virhash.h b/src/util/virhash.h index 8462cb3e3c..0170eaa3f2 100644 --- a/src/util/virhash.h +++ b/src/util/virhash.h @@ -56,12 +56,15 @@ typedef int (*virHashSearcher) (const void *payload, const void *name, /** * virHashKeyCode: * @name: the hash key + * @seed: random seed * - * Compute the hash code corresponding to the key @name + * Compute the hash code corresponding to the key @name, using + * @seed to perturb the hashing algorithm * * Returns the hash code */ -typedef uint32_t (*virHashKeyCode)(const void *name); +typedef uint32_t (*virHashKeyCode)(const void *name, + uint32_t seed); /** * virHashKeyEqual: * @namea: the first hash key diff --git a/src/util/virhashcode.c b/src/util/virhashcode.c new file mode 100644 index 0000000000..6d9179347d --- /dev/null +++ b/src/util/virhashcode.c @@ -0,0 +1,123 @@ +/* + * virhashcode.c: hash code generation + * + * Copyright (C) 2012 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * The hash code generation is based on the public domain MurmurHash3 from Austin Appleby: + * http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp + * + * We use only the 32 bit variant because the 2 produce different results while + * we need to produce the same result regardless of the architecture as + * clients can be both 64 or 32 bit at the same time. + */ + +#include + +#include "virhashcode.h" +#include "bitrotate.h" + +/* slower than original but handles platforms that do only aligned reads */ +__attribute__((always_inline)) +static uint32_t getblock(const uint8_t *p, int i) +{ + uint32_t r; + size_t size = sizeof(r); + + memcpy(&r, &p[i * size], size); + + return r; +} + +/* + * Finalization mix - force all bits of a hash block to avalanche + */ +__attribute__((always_inline)) +static uint32_t fmix(uint32_t h) +{ + h ^= h >> 16; + h *= 0x85ebca6b; + h ^= h >> 13; + h *= 0xc2b2ae35; + h ^= h >> 16; + + return h; +} + + +uint32_t virHashCodeGen(const void *key, size_t len, uint32_t seed) +{ + const uint8_t *blocks; + const uint8_t *tail; + size_t nblocks; + uint32_t h1; + uint32_t k1; + uint32_t c1; + uint32_t c2; + size_t i; + + blocks = (const uint8_t *)key; + nblocks = len / 4; + h1 = seed; + c1 = 0xcc9e2d51; + c2 = 0x1b873593; + + /* body */ + + for (i = 0; i < nblocks; i++) { + + k1 = getblock(blocks, i); + + k1 *= c1; + k1 = rotl32(k1, 15); + k1 *= c2; + + h1 ^= k1; + h1 = rotl32(h1, 13); + h1 = h1 * 5 + 0xe6546b64; + } + + /* tail */ + + tail = (const uint8_t *)key + nblocks * 4; + + k1 = 0; + + switch (len & 3) { + case 3: + k1 ^= tail[2] << 16; + /* fallthrough */ + case 2: + k1 ^= tail[1] << 8; + /* fallthrough */ + case 1: + k1 ^= tail[0]; + k1 *= c1; + k1 = rotl32(k1, 15); + k1 *= c2; + h1 ^= k1; + /* fallthrough */ + default: + break; + } + + /* finalization */ + + h1 ^= len; + h1 = fmix(h1); + + return h1; +} diff --git a/src/util/virhashcode.h b/src/util/virhashcode.h new file mode 100644 index 0000000000..867e04ea97 --- /dev/null +++ b/src/util/virhashcode.h @@ -0,0 +1,35 @@ +/* + * virhashcode.h: hash code generation + * + * Copyright (C) 2012 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * The hash code generation is based on the public domain MurmurHash3 from Austin Appleby: + * http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp + * + * We use only the 32 bit variant because the 2 produce different result while + * we need to produce the same result regardless of the architecture as + * clients can be both 64 or 32 bit at the same time. + */ + +#ifndef __VIR_HASH_CODE_H__ +# define __VIR_HASH_CODE_H__ + +# include "internal.h" + +extern uint32_t virHashCodeGen(const void *key, size_t len, uint32_t seed); + +#endif /* __VIR_HASH_CODE_H__ */