File: hash.go

package info (click to toggle)
golang-github-segmentio-fasthash 1.0.3-3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 132 kB
  • sloc: makefile: 2
file content (141 lines) | stat: -rw-r--r-- 3,240 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
package jody

import (
	"reflect"
	"unsafe"
)

const (
	shift    = 11
	constant = 0x1f3d5b79

	// Init64 is what 64 bits hash values should be initialized with.
	Init64 = uint64(0)
)

var mask64 = [...]uint64{
	0x0000000000000000,
	0x00000000000000ff,
	0x000000000000ffff,
	0x0000000000ffffff,
	0x00000000ffffffff,
	0x000000ffffffffff,
	0x0000ffffffffffff,
	0x00ffffffffffffff,
	0xffffffffffffffff,
}

// HashString64 returns the hash of s.
func HashString64(s string) uint64 {
	return AddString64(Init64, s)
}

// HashBytes64 returns the hash of u.
func HashBytes64(b []byte) uint64 {
	return AddBytes64(Init64, b)
}

// HashUint64 returns the hash of u.
func HashUint64(u uint64) uint64 {
	return AddUint64(Init64, u)
}

// AddString64 adds the hash of s to the precomputed hash value h.
func AddString64(h uint64, s string) uint64 {
	/*
		This is an implementation of the jody hashing algorithm as found here:

		- https://github.com/jbruchon/jodyhash

		It was revisited a bit and only the 64 bit version is available for now,
		but it's indeed really fast:

		- BenchmarkHash64/jody/algorithm-4   100000000   11.8 ns/op   3050.83 MB/s   0 B/op   0 allocs/op

		Here's what the reference implementation looks like:

		hash_t hash = start_hash;
		hash_t element;
		hash_t partial_salt;
		size_t len;

		len = count / sizeof(hash_t);
		for (; len > 0; len--) {
			element = *data;
			hash += element;
			hash += constant;
			hash = (hash << shift) | hash >> (sizeof(hash_t) * 8 - shift);
			hash ^= element;
			hash = (hash << shift) | hash >> (sizeof(hash_t) * 8 - shift);
			hash ^= constant;
			hash += element;
			data++;
		}

		len = count & (sizeof(hash_t) - 1);
		if (len) {
			partial_salt = constant & tail_mask[len];
			element = *data & tail_mask[len];
			hash += element;
			hash += partial_salt;
			hash = (hash << shift) | hash >> (sizeof(hash_t) * 8 - shift);
			hash ^= element;
			hash = (hash << shift) | hash >> (sizeof(hash_t) * 8 - shift);
			hash ^= partial_salt;
			hash += element;
		}

		return hash;
	*/

	r := *(*reflect.StringHeader)(unsafe.Pointer(&s))
	p := unsafe.Pointer((*reflect.StringHeader)(unsafe.Pointer(&s)).Data)

	for n := r.Len / 8; n != 0; n-- {
		v := *(*uint64)(p)

		h = h + v + constant
		h = (h<<shift | h>>(64-shift)) ^ v
		h = ((h<<shift | h>>(64-shift)) ^ constant) + v

		p = unsafe.Pointer(uintptr(p) + 8)
	}

	if n := (r.Len & 7); n != 0 {
		c := constant & mask64[n]
		v := uint64(0)
		off := uint(0)
		if 0 != (n & 4) {
			v += uint64(*(*uint32)(p))
			off += 32
			p = unsafe.Pointer(uintptr(p) + 4)
		}
		if 0 != (n & 2) {
			v += uint64(*(*uint16)(p)) << off
			off += 16
			p = unsafe.Pointer(uintptr(p) + 2)
		}
		if 0 != (n & 1) {
			v += uint64(*(*uint8)(p)) << off
		}

		h = h + v + c
		h = (h<<shift | h>>(64-shift)) ^ v
		h = ((h<<shift | h>>(64-shift)) ^ c) + v
	}

	return h
}

// AddBytes64 adds the hash of b to the precomputed hash value h.
func AddBytes64(h uint64, b []byte) uint64 {
	return AddString64(h, *(*string)(unsafe.Pointer(&b)))
}

// AddUint64 adds the hash value of the 8 bytes of u to h.
func AddUint64(h uint64, u uint64) uint64 {
	h = h + u + constant
	h = (h<<shift | h>>(64-shift)) ^ u
	h = ((h<<shift | h>>(64-shift)) ^ constant) + u
	return h
}