diff options
Diffstat (limited to 'contrib/fmhash/fmhash.c')
-rw-r--r-- | contrib/fmhash/fmhash.c | 384 |
1 files changed, 384 insertions, 0 deletions
diff --git a/contrib/fmhash/fmhash.c b/contrib/fmhash/fmhash.c new file mode 100644 index 0000000..224b824 --- /dev/null +++ b/contrib/fmhash/fmhash.c @@ -0,0 +1,384 @@ +/* + * Copyright (c) 2018, Harshvardhan Shrivastava + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include <stdint.h> +#include <stddef.h> +#ifndef _AIX +#include <typedefs.h> +#endif +#include <sys/types.h> +#include <string.h> +#ifdef USE_HASH_XXHASH +# include <xxhash.h> +#endif + +#include "rsyslog.h" +#include "parserif.h" +#include "module-template.h" +#include "rainerscript.h" + + + +MODULE_TYPE_FUNCTION +MODULE_TYPE_NOKEEP +DEF_FMOD_STATIC_DATA + +typedef uint64_t hash_t; +typedef uint32_t seed_t; +typedef struct hash_context_s hash_context_t; + +typedef hash_t (*hash_impl)(const void*, size_t, seed_t); + +typedef rsRetVal (*hash_wrapper_2)(struct svar *__restrict__ const + , struct svar *__restrict__ const, hash_context_t*, hash_t*); +typedef rsRetVal (*hash_wrapper_3)(struct svar *__restrict__ const, struct svar *__restrict__ const + , struct svar *__restrict__ const, hash_context_t*, hash_t*); + +struct hash_context_s { + hash_impl hashXX; + hash_wrapper_2 hash_wrapper_1_2; + hash_wrapper_3 hash_wrapper_2_3; +}; + +/* + * Fowler–Noll–Vo hash 32 bit + * http://www.isthe.com/chongo/src/fnv/hash_32.c + */ +#if defined(__clang__) +#pragma GCC diagnostic ignored "-Wunknown-attributes" +#endif +static hash_t +#if defined(__clang__) +__attribute__((no_sanitize("unsigned-integer-overflow"))) +#endif +fnv_32(const void* input, size_t len, seed_t seed) { + unsigned char *bp = (unsigned char *)input; /* start of buffer */ + + /* + * FNV-1 hash each octet in the buffer + */ + size_t i; + for (i = 0; i < len; i++) { + /* multiply by the 32 bit FNV magic prime mod 2^32 */ + seed += (seed<<1) + (seed<<4) + (seed<<7) + (seed<<8) + (seed<<24); + + /* xor the bottom with the current octet */ + seed ^= (seed_t)*bp++; + } + + /* return our new hash value */ + return seed; +} + + +/* + * Modified Bernstein + * http://www.eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx + */ +#if defined(__clang__) +#pragma GCC diagnostic ignored "-Wunknown-attributes" +#endif +static hash_t +#if defined(__clang__) +__attribute__((no_sanitize("unsigned-integer-overflow"))) +#endif +djb_hash(const void* input, size_t len, seed_t seed) { + const char *p = input; + hash_t hash = 5381; + size_t i; + for (i = 0; i < len; i++) { + hash = 33 * hash ^ p[i]; + } + + return hash + seed; +} + +/*Get 32 bit hash for input*/ +static hash_t +hash32(const void* input, size_t len, seed_t seed) { + hash_t xhash = 0; +#ifdef USE_HASH_XXHASH + xhash = XXH32(input, len, seed); +#else + xhash = fnv_32(input, len, seed); +#endif + return xhash; +} + +/*Get 64 bit hash for input*/ +static hash_t +hash64(const void* input, size_t len, seed_t seed) { + hash_t xhash = 0; +#ifdef USE_HASH_XXHASH + xhash = XXH64(input, len, seed); +#else + xhash = djb_hash(input, len, seed); +#endif + return xhash; +} + +static rsRetVal +hash_wrapper2(struct svar *__restrict__ const sourceVal + , struct svar *__restrict__ const seedVal, hash_context_t* hcontext, hash_t* xhash) { + DEFiRet; + int freeHashStr = 0, success = 0; + char *hashStr = NULL; + seed_t seed = 0; + if(seedVal) { + seed = var2Number(seedVal, &success); + if (!success) { + parser_warnmsg("fmhash: hashXX(string, seed) didn't get a valid 'seed' limit" + ", defaulting hash value to 0"); + ABORT_FINALIZE(RS_RET_PARAM_ERROR); + } + } + + hashStr = (char*)var2CString(sourceVal, &freeHashStr); + size_t len = strlen(hashStr); + (*xhash) = hcontext->hashXX(hashStr, len, seed); + DBGPRINTF("fmhash: hashXX generated hash %" PRIu64 " for string(%.*s)" + , (*xhash), (int)len, hashStr); +finalize_it: + if (freeHashStr) { + free(hashStr); + } + RETiRet; +} + +static rsRetVal +hash_wrapper3(struct svar *__restrict__ const sourceVal, struct svar *__restrict__ const modVal + , struct svar *__restrict__ const seedVal, hash_context_t* hcontext, hash_t* xhash) { + + DEFiRet; + int success = 0; + hash_t mod = var2Number(modVal, &success); + if (! success) { + parser_warnmsg("fmhash: hashXXmod(string, mod)/hash64mod(string, mod, seed) didn't" + " get a valid 'mod' limit, defaulting hash value to 0"); + ABORT_FINALIZE(RS_RET_PARAM_ERROR); + } + if(mod == 0) { + parser_warnmsg("fmhash: hashXXmod(string, mod)/hash64mod(string, mod, seed) invalid" + ", 'mod' is zero, , defaulting hash value to 0"); + ABORT_FINALIZE(RS_RET_PARAM_ERROR); + } + + CHKiRet((hcontext->hash_wrapper_1_2(sourceVal, seedVal, hcontext, xhash))); + if(mod != 0) { + (*xhash) = (*xhash) % mod; + } + DBGPRINTF("fmhash: hashXXmod generated hash-mod %" PRIu64 ".", (*xhash)); +finalize_it: + RETiRet; +} + +static void +init_hash32_context(hash_context_t* hash32_context) { + hash32_context->hashXX = hash32; + hash32_context->hash_wrapper_1_2 = hash_wrapper2; + hash32_context->hash_wrapper_2_3 = hash_wrapper3; +}; + +static void +init_hash64_context(hash_context_t* hash64_context) { + hash64_context->hashXX = hash64; + hash64_context->hash_wrapper_1_2 = hash_wrapper2; + hash64_context->hash_wrapper_2_3 = hash_wrapper3; +}; + +static void ATTR_NONNULL() +fmHashXX(struct cnffunc *__restrict__ const func, struct svar *__restrict__ const ret, + void *__restrict__ const usrptr, wti_t *__restrict__ const pWti) { + DEFiRet; + struct svar hashStrVal; + struct svar seedVal; + hash_context_t* hcontext = NULL; + hash_t xhash = 0; + cnfexprEval(func->expr[0], &hashStrVal, usrptr, pWti); + if(func->nParams == 2) cnfexprEval(func->expr[1], &seedVal, usrptr, pWti); + ret->d.n = 0; + ret->datatype = 'N'; + hcontext = (hash_context_t*) func->funcdata; + CHKiRet((hcontext->hash_wrapper_1_2(&hashStrVal + , (func->nParams == 2 ? &seedVal : NULL) + , hcontext, &xhash))); + ret->d.n = xhash; +finalize_it: + varFreeMembers(&hashStrVal); + if(func->nParams == 2) varFreeMembers(&seedVal); +} + +static void ATTR_NONNULL() +fmHashXXmod(struct cnffunc *__restrict__ const func, struct svar *__restrict__ const ret, + void *__restrict__ const usrptr, wti_t *__restrict__ const pWti) { + + DEFiRet; + struct svar hashStrVal; + struct svar modVal; + struct svar seedVal; + hash_context_t* hcontext = NULL; + hash_t xhash = 0; + cnfexprEval(func->expr[0], &hashStrVal, usrptr, pWti); + cnfexprEval(func->expr[1], &modVal, usrptr, pWti); + if(func->nParams == 3) cnfexprEval(func->expr[2], &seedVal, usrptr, pWti); + ret->d.n = 0; + ret->datatype = 'N'; + hcontext = (hash_context_t*) func->funcdata; + CHKiRet((hcontext->hash_wrapper_2_3(&hashStrVal + , &modVal, func->nParams > 2 ? &seedVal : NULL + , hcontext, &xhash))); + ret->d.n = xhash; +finalize_it: + varFreeMembers(&hashStrVal); + varFreeMembers(&modVal); + if(func->nParams == 3) varFreeMembers(&seedVal); +} + +static inline sbool check_param_count_hash(unsigned short nParams) { + return (nParams != 1 && nParams != 2); +} + +static inline sbool check_param_count_hashmod(unsigned short nParams) { + return (nParams != 2 && nParams != 3); +} + +static rsRetVal ATTR_NONNULL(1) +init_fmHash64(struct cnffunc *const func) +{ + DEFiRet; + hash_context_t *hash_context = NULL; + if(check_param_count_hash(func->nParams)) { + parser_errmsg("fmhash: hash64(string) / hash64(string, seed)" + " insufficient params.\n"); + ABORT_FINALIZE(RS_RET_INVLD_NBR_ARGUMENTS); + } + func->destructable_funcdata = 1; + CHKmalloc(hash_context = calloc(1, sizeof(hash_context_t))); + init_hash64_context(hash_context); + func->funcdata = (void*)hash_context; + +finalize_it: + RETiRet; +} + +static rsRetVal ATTR_NONNULL(1) +init_fmHash64mod(struct cnffunc *const func) +{ + DEFiRet; + hash_context_t *hash_context = NULL; + if(check_param_count_hashmod(func->nParams)) { + parser_errmsg("fmhash: hash64mod(string, mod)/hash64mod(string, mod, seed)" + " insufficient params.\n"); + ABORT_FINALIZE(RS_RET_INVLD_NBR_ARGUMENTS); + } + func->destructable_funcdata = 1; + CHKmalloc(hash_context = calloc(1, sizeof(hash_context_t))); + init_hash64_context(hash_context); + func->funcdata = (void*)hash_context; +finalize_it: + RETiRet; +} + +static rsRetVal ATTR_NONNULL(1) +init_fmHash32(struct cnffunc *const func) +{ + DEFiRet; + hash_context_t *hash_context = NULL; + if(check_param_count_hash(func->nParams)) { + parser_errmsg("fmhash: hash32(string) / hash32(string, seed)" + " insufficient params.\n"); + ABORT_FINALIZE(RS_RET_INVLD_NBR_ARGUMENTS); + } + func->destructable_funcdata = 1; + CHKmalloc(hash_context = calloc(1, sizeof(hash_context_t))); + init_hash32_context(hash_context); + func->funcdata = (void*)hash_context; + +finalize_it: + RETiRet; +} + +static rsRetVal ATTR_NONNULL(1) +init_fmHash32mod(struct cnffunc *const func) +{ + DEFiRet; + hash_context_t *hash_context = NULL; + if(check_param_count_hashmod(func->nParams)) { + parser_errmsg("fmhash: hash32mod(string, mod)/hash32mod(string, mod, seed)" + " insufficient params.\n"); + ABORT_FINALIZE(RS_RET_INVLD_NBR_ARGUMENTS); + } + func->destructable_funcdata = 1; + CHKmalloc(hash_context = calloc(1, sizeof(hash_context_t))); + init_hash32_context(hash_context); + func->funcdata = (void*)hash_context; +finalize_it: + RETiRet; +} + + +static struct scriptFunct functions[] = { + {"hash64", 1, 2, fmHashXX, init_fmHash64, NULL}, + {"hash64mod", 2, 3, fmHashXXmod, init_fmHash64mod, NULL}, + {"hash32", 1, 2, fmHashXX, init_fmHash32, NULL}, + {"hash32mod", 2, 3, fmHashXXmod, init_fmHash32mod, NULL}, + {NULL, 0, 0, NULL, NULL, NULL} //last element to check end of array +}; + + +BEGINgetFunctArray +CODESTARTgetFunctArray + dbgprintf("Hash: fmhhash\n"); + *version = 1; + *functArray = functions; +ENDgetFunctArray + + +BEGINmodExit +CODESTARTmodExit +ENDmodExit + + +BEGINqueryEtryPt +CODESTARTqueryEtryPt +CODEqueryEtryPt_STD_FMOD_QUERIES +ENDqueryEtryPt + + +BEGINmodInit() +CODESTARTmodInit + *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ +CODEmodInit_QueryRegCFSLineHdlr + dbgprintf("rsyslog fmhash init called, compiled with version %s\n", VERSION); +ENDmodInit |