Skip to content
This repository was archived by the owner on Feb 1, 2019. It is now read-only.

Commit 01e85ef

Browse files
committed
Base HRW implementation in golang
1 parent ca07143 commit 01e85ef

File tree

7 files changed

+453
-2
lines changed

7 files changed

+453
-2
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.idea
2+
.vscode
3+
*.out

README.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
# hrw
2-
Golang simple HRW implementation
1+
# Golang simple HRW implementation

go.mod

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module github.com/im-kulikov/hrw
2+
3+
require github.com/reusee/mmh3 v0.0.0-20140820141314-64b85163255b

go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
github.com/reusee/mmh3 v0.0.0-20140820141314-64b85163255b h1:GQkEnyBFqzQXb3RFqGt5z2QcBZJVQxgzXKF/sPCFh7w=
2+
github.com/reusee/mmh3 v0.0.0-20140820141314-64b85163255b/go.mod h1:ADBBIMrt68BC/v967NyoiPZMwPVq44r8QJ5oRyXJHJs=

hrw.go

+138
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
// Package hrw implements Rendezvous hashing.
2+
// http://en.wikipedia.org/wiki/Rendezvous_hashing.
3+
package hrw
4+
5+
import (
6+
"errors"
7+
"hash/fnv"
8+
"reflect"
9+
"sort"
10+
"strconv"
11+
)
12+
13+
type (
14+
swapper func(i, j int)
15+
16+
Hasher interface{ Hash() uint64 }
17+
18+
hashed struct {
19+
length int
20+
sorted []uint64
21+
weight []uint64
22+
}
23+
)
24+
25+
const m64 = 18446744073709551615 // modulus (2**64-1)
26+
27+
func weight(x uint64, y uint64) uint64 {
28+
acc := x ^ y
29+
acc ^= acc >> 33
30+
acc = (acc * 0xff51afd7ed558ccd) % m64
31+
acc ^= acc >> 33
32+
acc = (acc * 0xc4ceb9fe1a85ec53) % m64
33+
acc ^= acc >> 33
34+
return acc
35+
}
36+
37+
func (h hashed) Len() int { return h.length }
38+
func (h hashed) Less(i, j int) bool { return h.weight[h.sorted[i]] < h.weight[h.sorted[j]] }
39+
func (h hashed) Swap(i, j int) { h.sorted[i], h.sorted[j] = h.sorted[j], h.sorted[i] }
40+
41+
func SortByWeight(nodes []uint64, hash uint64) []uint64 {
42+
var (
43+
l = len(nodes)
44+
h = hashed{
45+
length: l,
46+
sorted: make([]uint64, 0, l),
47+
weight: make([]uint64, 0, l),
48+
}
49+
)
50+
51+
for i, node := range nodes {
52+
h.sorted = append(h.sorted, uint64(i))
53+
h.weight = append(h.weight, weight(node, hash))
54+
}
55+
56+
sort.Sort(h)
57+
return h.sorted
58+
}
59+
60+
func SortSliceByValue(slice interface{}, hash uint64) error {
61+
t := reflect.TypeOf(slice)
62+
if t.Kind() != reflect.Slice {
63+
return errors.New("must be slice")
64+
}
65+
66+
var (
67+
val = reflect.ValueOf(slice)
68+
swap = reflect.Swapper(slice)
69+
length = val.Len()
70+
rule = make([]uint64, 0, length)
71+
)
72+
73+
if length == 0 {
74+
return nil
75+
}
76+
77+
switch slice := slice.(type) {
78+
case []int:
79+
hasher := fnv.New64()
80+
for i := 0; i < length; i++ {
81+
hasher.Reset()
82+
// error always nil
83+
_, _ = hasher.Write([]byte(strconv.Itoa(slice[i])))
84+
rule = append(rule, weight(hash, hasher.Sum64()))
85+
}
86+
case []string:
87+
hasher := fnv.New64()
88+
for i := 0; i < length; i++ {
89+
hasher.Reset()
90+
// error always nil
91+
_, _ = hasher.Write([]byte(slice[i]))
92+
rule = append(rule, weight(hash, hasher.Sum64()))
93+
}
94+
default:
95+
if _, ok := val.Index(0).Interface().(Hasher); !ok {
96+
return errors.New("unknown type")
97+
}
98+
99+
for i := 0; i < length; i++ {
100+
h := val.Index(i).Interface().(Hasher)
101+
rule = append(rule, weight(hash, h.Hash()))
102+
}
103+
}
104+
105+
rule = SortByWeight(rule, hash)
106+
sortByRule(swap, uint64(length), rule)
107+
108+
return nil
109+
}
110+
111+
func SortSliceByIndex(slice interface{}, hash uint64) {
112+
length := uint64(reflect.ValueOf(slice).Len())
113+
swap := reflect.Swapper(slice)
114+
115+
rule := make([]uint64, 0, length)
116+
for i := uint64(0); i < length; i++ {
117+
rule = append(rule, i)
118+
}
119+
120+
rule = SortByWeight(rule, hash)
121+
sortByRule(swap, length, rule)
122+
}
123+
124+
func sortByRule(swap swapper, length uint64, rule []uint64) {
125+
done := make([]bool, length)
126+
for i := uint64(0); i < length; i++ {
127+
if done[i] {
128+
continue
129+
}
130+
131+
done[i] = true
132+
133+
for j := rule[i]; !done[rule[j]]; j = rule[j] {
134+
swap(int(i), int(j))
135+
done[j] = true
136+
}
137+
}
138+
}

hrw.test

3 MB
Binary file not shown.

0 commit comments

Comments
 (0)