package hash32

/*
 * ***注意***
 *
 *  Sum 使用的是[]byte参数；string中文是utf-8编码
 *  SumByRune 使用的是[]rune参数；中文使用的是unicode编码
 *
 *  两种参数中文编码不同；同一个string调用两个接口得到的hash是不同的！！！ 这是要特别注意的
 *
 *  对string进行for range操作会自动被[]rune化；一定要注意！！！
 *
 */

const shiftBit = 11 // 每个字节移位数(测试经验值11)
const reverseBit = 32 - shiftBit

// 快速Hash计算(*** 注意：只取了rune低16位进行hash计算；计算结果与SumByRune不一致 ***)
func FastSumByRune2(in rune, hs uint32) (out uint32) {
	out = ((hs << shiftBit) | (hs >> reverseBit)) + uint32(byte(in>>8))
	out = ((out << shiftBit) | (out >> reverseBit)) + uint32(byte(in))
	return
}

// 快速Hash计算(*** 此计算结果与SumByRune一致 ***)
func FastSumByRune4(in rune, hs uint32) (out uint32) {
	out = ((hs << shiftBit) | (hs >> reverseBit)) + uint32(byte(in>>24))
	out = ((out << shiftBit) | (out >> reverseBit)) + uint32(byte(in>>16))
	out = ((out << shiftBit) | (out >> reverseBit)) + uint32(byte(in>>8))
	out = ((out << shiftBit) | (out >> reverseBit)) + uint32(byte(in))
	return
}

// 原Hash值参数重
func hsArg(hsOpt ...uint32) (out uint32) {
	out = uint32(0)
	if len(hsOpt) > 0 {
		out = hsOpt[0]
	}
	return
}

// Hash计算
// in - 待hash串
// hsOpt - 原hash值(在此基础上继续hash)
func Sum(in []byte, hsOpt ...uint32) (out uint32) {
	out = hsArg(hsOpt...)
	for _, v := range in {
		out = ((out << shiftBit) | (out >> reverseBit)) + uint32(v)
	}

	return
}

// Hash计算
func SumByRune(in rune, hsOpt ...uint32) (out uint32) {
	// rune转[]byte
	inVal := make([]byte, 4)
	inVal[0] = byte(in >> 24)
	inVal[1] = byte(in >> 16)
	inVal[2] = byte(in >> 8)
	inVal[3] = byte(in)

	// *** 经实际测试：不加以下代码运行效率更高 ***

	// 去除前面多余的\x00
	// for {
	// 	if len(inVal) <= 1 {
	// 		// 以免全0异常；至少要保留1位
	// 		break
	// 	}
	// 	if inVal[0] == 0 {
	// 		inVal = inVal[1:]
	// 	} else {
	// 		break
	// 	}
	// }
	// 规避hash冲突，如："N-"与"中"unicode编码均为#4E2D，即会产生hash冲突
	// 若长度>1(即非常规ASCII)，所有字节最高位置1
	// if len(inVal) > 1 {
	// 	for i := 0; i < len(inVal); i++ {
	// 		inVal[i] |= 0x80
	// 	}
	// }
	out = Sum(inVal, hsOpt...)

	return
}

// Hash计算
func SumByRunes(in []rune, hsOpt ...uint32) (out uint32) {
	out = hsArg(hsOpt...)
	for _, v := range in {
		out = SumByRune(v, out)
	}

	return
}
