/*
 * Copyright (C) 2006 iptelorg GmbH
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
/*!
* \file
* \brief Kamailio core :: hash support
* \author Andrei
* \ingroup core
* Module: \ref core
*/


#ifndef _hashes_h
#define _hashes_h

#include "str.h"

#define core_hash_idx(hid, hsize) ((hid) & (hsize - 1))

/** internal use: hash update
 * params: char* s   - string start,
 *         char* end - end
 *         char* p,  and unsigned v temporary vars (used)
 *         unsigned h - result
 * h should be initialized (e.g. set it to 0), the result in h */
#define hash_update_str(s, end, p, v, h)                             \
	do {                                                             \
		for((p) = (s); (p) <= ((end) - 4); (p) += 4) {               \
			(v) = (((unsigned int)(unsigned char)(p)[0]) << 24)      \
				  + (((unsigned int)(unsigned char)(p)[1]) << 16)    \
				  + (((unsigned int)(unsigned char)(p)[2]) << 8)     \
				  + (unsigned int)(unsigned char)(p)[3];             \
			(h) += (v) ^ ((v) >> 3);                                 \
		}                                                            \
		switch((end) - (p)) {                                        \
			case 3:                                                  \
				(v) = (((unsigned int)(unsigned char)(p)[0]) << 16)  \
					  + (((unsigned int)(unsigned char)(p)[1]) << 8) \
					  + (unsigned int)(unsigned char)(p)[2];         \
				break;                                               \
			case 2:                                                  \
				(v) = (((unsigned int)(unsigned char)(p)[0]) << 8)   \
					  + (unsigned int)(unsigned char)p[1];           \
				break;                                               \
			case 1:                                                  \
				(v) = (unsigned int)(unsigned char)*p;               \
				break;                                               \
			default:                                                 \
				(v) = 0;                                             \
				break;                                               \
		}                                                            \
		(h) += (v) ^ ((v) >> 3);                                     \
	} while(0)

/** like hash_update_str, but case insensitive
 * params: char* s   - string start,
 *         char* end - end
 *         char* p,  and unsigned v temporary vars (used)
 *         unsigned h - result
 * h should be initialized (e.g. set it to 0), the result in h */
#define hash_update_case_str(s, end, p, v, h)                             \
	do {                                                                  \
		for((p) = (s); (p) <= ((end) - 4); (p) += 4) {                    \
			(v) = ((((unsigned int)(unsigned char)(p)[0]) << 24)          \
						  + (((unsigned int)(unsigned char)(p)[1]) << 16) \
						  + (((unsigned int)(unsigned char)(p)[2]) << 8)  \
						  + (unsigned int)(unsigned char)(p)[3])          \
				  | 0x20202020;                                           \
			(h) += (v) ^ ((v) >> 3);                                      \
		}                                                                 \
		(v) = 0;                                                          \
		for(; (p) < (end); (p)++) {                                       \
			(v) <<= 8;                                                    \
			(v) += ((unsigned int)(unsigned char)*(p)) | 0x20;            \
		}                                                                 \
		(h) += (v) ^ ((v) >> 3);                                          \
	} while(0)


/** internal use: call it to adjust the h from hash_update_str */
#define hash_finish(h) (((h) + ((h) >> 11)) + (((h) >> 13) + ((h) >> 23)))


/** "raw" 2 strings hash
 * returns an unsigned int (which you can use modulo table_size as hash value)
 */
inline static unsigned int get_hash2_raw(const str *key1, const str *key2)
{
	char *p;
	register unsigned v;
	register unsigned h;

	h = 0;

	hash_update_str(key1->s, key1->s + key1->len, p, v, h);
	hash_update_str(key2->s, key2->s + key2->len, p, v, h);
	return hash_finish(h);
}


/** "raw" 1 string hash
 * returns an unsigned int (which you can use modulo table_size as hash value)
 */
inline static unsigned int get_hash1_raw(const char *s, int len)
{
	const char *p;
	register unsigned v;
	register unsigned h;

	h = 0;

	hash_update_str(s, s + len, p, v, h);
	return hash_finish(h);
}


/** a little slower than hash_* , but better distribution for
 * numbers and about the same for strings */
#define hash_update_str2(s, end, p, v, h)                            \
	do {                                                             \
		for((p) = (s); (p) <= ((end) - 4); (p) += 4) {               \
			(v) = (((unsigned int)(unsigned char)(p)[0]) * 16777213) \
				  + (((unsigned int)(unsigned char)(p)[1]) * 65537)  \
				  + (((unsigned int)(unsigned char)(p)[2]) * 257)    \
				  + (unsigned int)(unsigned char)(p)[3];             \
			(h) = 16777259 * (h) + ((v) ^ ((v) << 17));              \
		}                                                            \
		(v) = 0;                                                     \
		for(; (p) < (end); (p)++) {                                  \
			(v) *= 251;                                              \
			(v) += (unsigned int)(unsigned char)*(p);                \
		}                                                            \
		(h) = 16777259 * (h) + ((v) ^ ((v) << 17));                  \
	} while(0)

/**  like hash_update_str2 but case insensitive */
#define hash_update_case_str2(s, end, p, v, h)                                \
	do {                                                                      \
		for((p) = (s); (p) <= ((end) - 4); (p) += 4) {                        \
			(v) = ((((unsigned int)(unsigned char)(p)[0]) | 0x20) * 16777213) \
				  + ((((unsigned int)(unsigned char)(p)[1]) | 0x20) * 65537)  \
				  + ((((unsigned int)(unsigned char)(p)[2]) | 0x20) * 257)    \
				  + (((unsigned int)(unsigned char)(p)[3]) | 0x20);           \
			(h) = 16777259 * (h) + ((v) ^ ((v) << 17));                       \
		}                                                                     \
		(v) = 0;                                                              \
		for(; (p) < (end); (p)++) {                                           \
			(v) *= 251;                                                       \
			(v) += ((unsigned int)(unsigned char)(p)[0]) | 0x20;              \
		}                                                                     \
		(h) = 16777259 * (h) + ((v) ^ ((v) << 17));                           \
	} while(0)

/** internal use: call it to adjust the h from hash_update_str */
#define hash_finish2(h) (((h) + ((h) >> 7)) + (((h) >> 13) + ((h) >> 23)))


/** a little slower than get_hash1_raw() , but better distribution for
 * numbers and about the same for strings */
inline static unsigned int get_hash1_raw2(const char *s, int len)
{
	const char *p;
	register unsigned v;
	register unsigned h;

	h = 0;

	hash_update_str2(s, s + len, p, v, h);
	return hash_finish2(h);
}


/* "raw" 2 strings hash optimized for numeric strings (see above)
 * returns an unsigned int (which you can use modulo table_size as hash value)
 */
inline static unsigned int get_hash2_raw2(const str *key1, const str *key2)
{
	char *p;
	register unsigned v;
	register unsigned h;

	h = 0;

	hash_update_str2(key1->s, key1->s + key1->len, p, v, h);
	hash_update_str2(key2->s, key2->s + key2->len, p, v, h);
	return hash_finish2(h);
}


/* "raw" 2 strings case insensitive hash (like get_hash2_raw but case
 * insensitive)
 * returns an unsigned int (which you can use modulo table_size as hash value)
 */
inline static unsigned int get_hash2_case_raw(const str *key1, const str *key2)
{
	char *p;
	register unsigned v;
	register unsigned h;

	h = 0;

	hash_update_case_str(key1->s, key1->s + key1->len, p, v, h);
	hash_update_case_str(key2->s, key2->s + key2->len, p, v, h);
	return hash_finish(h);
}


/* "raw" 1 string case insensitive hash
 * returns an unsigned int (which you can use modulo table_size as hash value)
 */
inline static unsigned int get_hash1_case_raw(const char *s, int len)
{
	const char *p;
	register unsigned v;
	register unsigned h;

	h = 0;

	hash_update_case_str(s, s + len, p, v, h);
	return hash_finish(h);
}


/* same as get_hash1_raw2, but case insensitive and slower
 * returns an unsigned int (which you can use modulo table_size as hash value)
 */
inline static unsigned int get_hash1_case_raw2(const char *s, int len)
{
	const char *p;
	register unsigned v;
	register unsigned h;

	h = 0;

	hash_update_case_str2(s, s + len, p, v, h);
	return hash_finish2(h);
}


/* "raw" 2 strings hash optimized for numeric strings (see above)
 * same as get_hash2_raw2 but case insensitive and slower
 * returns an unsigned int (which you can use modulo table_size as hash value)
 */
inline static unsigned int get_hash2_case_raw2(const str *key1, const str *key2)
{
	char *p;
	register unsigned v;
	register unsigned h;

	h = 0;

	hash_update_case_str2(key1->s, key1->s + key1->len, p, v, h);
	hash_update_case_str2(key2->s, key2->s + key2->len, p, v, h);
	return hash_finish2(h);
}


/*
 * generic hashing - from the initial origins of ser
 */
#define ch_h_inc h += v ^ (v >> 3)
#define ch_icase(_c) (((_c) >= 'A' && (_c) <= 'Z') ? ((_c) | 0x20) : (_c))

/*
 * case sensitive hashing
 * - s1 - str to hash
 * - s2 - optional - continue hashing over s2
 * - size - optional - size of hash table (must be power of 1); if set (!=0),
 *   instead of hash id, returned value is slot index
 * return computed hash id or hash table slot index
 */
static inline unsigned int core_hash(
		const str *s1, const str *s2, const unsigned int size)
{
	char *p, *end;
	register unsigned v;
	register unsigned h;

	h = 0;

	end = s1->s + s1->len;
	for(p = s1->s; p <= (end - 4); p += 4) {
		v = ((unsigned int)(unsigned char)p[0] << 24)
			+ ((unsigned int)(unsigned char)p[1] << 16)
			+ ((unsigned int)(unsigned char)p[2] << 8)
			+ (unsigned int)(unsigned char)p[3];
		ch_h_inc;
	}
	v = 0;
	for(; p < end; p++) {
		v <<= 8;
		v += (unsigned int)(unsigned char)*p;
	}
	ch_h_inc;

	if(s2) {
		end = s2->s + s2->len;
		for(p = s2->s; p <= (end - 4); p += 4) {
			v = ((unsigned int)(unsigned char)p[0] << 24)
				+ ((unsigned int)(unsigned char)p[1] << 16)
				+ ((unsigned int)(unsigned char)p[2] << 8)
				+ (unsigned int)(unsigned char)p[3];
			ch_h_inc;
		}
		v = 0;
		for(; p < end; p++) {
			v <<= 8;
			v += (unsigned int)(unsigned char)p[0];
		}
		ch_h_inc;
	}
	h = ((h) + (h >> 11)) + ((h >> 13) + (h >> 23));
	return size ? ((h) & (size - 1)) : h;
}


/*
 * case insensitive hashing
 * - s1 - str to hash
 * - s2 - optional - continue hashing over s2
 * - size - optional - size of hash table (must be power of 1); if set (!=0),
 *   instead of hash id, returned value is slot index
 * return computed hash id or hash table slot index
 */
static inline unsigned int core_case_hash(str *s1, str *s2, unsigned int size)
{
	char *p, *end;
	register unsigned v;
	register unsigned h;

	h = 0;

	end = s1->s + s1->len;
	for(p = s1->s; p <= (end - 4); p += 4) {
		v = (ch_icase(*p) << 24) + (ch_icase(p[1]) << 16)
			+ (ch_icase(p[2]) << 8) + ch_icase(p[3]);
		ch_h_inc;
	}
	v = 0;
	for(; p < end; p++) {
		v <<= 8;
		v += ch_icase(*p);
	}
	ch_h_inc;

	if(s2) {
		end = s2->s + s2->len;
		for(p = s2->s; p <= (end - 4); p += 4) {
			v = (ch_icase(*p) << 24) + (ch_icase(p[1]) << 16)
				+ (ch_icase(p[2]) << 8) + ch_icase(p[3]);
			ch_h_inc;
		}
		v = 0;
		for(; p < end; p++) {
			v <<= 8;
			v += ch_icase(*p);
		}
		ch_h_inc;
	}
	h = ((h) + (h >> 11)) + ((h >> 13) + (h >> 23));
	return size ? ((h) & (size - 1)) : h;
}


#endif
