//go:build runtime_memhash_leveldb || (wasi && !runtime_memhash_fnv && !runtime_memhash_tsip)

// This is the default for WASI, but can also be used on other targets with the
// right build tag.

// This is the hash function from Google's leveldb key-value storage system. It
// processes 4 bytes at a time making it faster than the FNV hash for buffer
// lengths > 16 bytes.

// https://github.com/google/leveldb
// https://en.wikipedia.org/wiki/LevelDB

package runtime

import (
	"unsafe"
)

func ptrToSlice(ptr unsafe.Pointer, n uintptr) []byte {
	var p []byte

	type _bslice struct {
		ptr *byte
		len uintptr
		cap uintptr
	}

	pslice := (*_bslice)(unsafe.Pointer(&p))
	pslice.ptr = (*byte)(ptr)
	pslice.cap = n
	pslice.len = n

	return p
}

// leveldb hash
func hash32(ptr unsafe.Pointer, n, seed uintptr) uint32 {

	const (
		lseed = 0xbc9f1d34
		m     = 0xc6a4a793
	)

	b := ptrToSlice(ptr, n)

	h := uint32(lseed^seed) ^ uint32(uint(len(b))*uint(m))

	for ; len(b) >= 4; b = b[4:] {
		h += uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
		h *= m
		h ^= h >> 16
	}
	switch len(b) {
	case 3:
		h += uint32(b[2]) << 16
		fallthrough
	case 2:
		h += uint32(b[1]) << 8
		fallthrough
	case 1:
		h += uint32(b[0])
		h *= m
		h ^= h >> 24
	}

	return h
}

// hash64finalizer is a 64-bit integer mixing function from
// https://web.archive.org/web/20120720045250/http://www.cris.com/~Ttwang/tech/inthash.htm
func hash64finalizer(key uint64) uint64 {
	key = ^key + (key << 21) // key = (key << 21) - key - 1;
	key = key ^ (key >> 24)
	key = (key + (key << 3)) + (key << 8) // key * 265
	key = key ^ (key >> 14)
	key = (key + (key << 2)) + (key << 4) // key * 21
	key = key ^ (key >> 28)
	key = key + (key << 31)
	return key
}

// hash64 turns hash32 into a 64-bit hash, by use hash64finalizer to
// mix the result of hash32 function combined with an xorshifted version of
// the seed.
func hash64(ptr unsafe.Pointer, n, seed uintptr) uint64 {
	h32 := hash32(ptr, n, seed)
	return hash64finalizer((uint64(h32^xorshift32(uint32(seed))) << 32) | uint64(h32))
}
