| package rendezvous |
| |
| type Rendezvous struct { |
| nodes map[string]int |
| nstr []string |
| nhash []uint64 |
| hash Hasher |
| } |
| |
| type Hasher func(s string) uint64 |
| |
| func New(nodes []string, hash Hasher) *Rendezvous { |
| r := &Rendezvous{ |
| nodes: make(map[string]int, len(nodes)), |
| nstr: make([]string, len(nodes)), |
| nhash: make([]uint64, len(nodes)), |
| hash: hash, |
| } |
| |
| for i, n := range nodes { |
| r.nodes[n] = i |
| r.nstr[i] = n |
| r.nhash[i] = hash(n) |
| } |
| |
| return r |
| } |
| |
| func (r *Rendezvous) Lookup(k string) string { |
| // short-circuit if we're empty |
| if len(r.nodes) == 0 { |
| return "" |
| } |
| |
| khash := r.hash(k) |
| |
| var midx int |
| var mhash = xorshiftMult64(khash ^ r.nhash[0]) |
| |
| for i, nhash := range r.nhash[1:] { |
| if h := xorshiftMult64(khash ^ nhash); h > mhash { |
| midx = i + 1 |
| mhash = h |
| } |
| } |
| |
| return r.nstr[midx] |
| } |
| |
| func (r *Rendezvous) Add(node string) { |
| r.nodes[node] = len(r.nstr) |
| r.nstr = append(r.nstr, node) |
| r.nhash = append(r.nhash, r.hash(node)) |
| } |
| |
| func (r *Rendezvous) Remove(node string) { |
| // find index of node to remove |
| nidx := r.nodes[node] |
| |
| // remove from the slices |
| l := len(r.nstr) |
| r.nstr[nidx] = r.nstr[l] |
| r.nstr = r.nstr[:l] |
| |
| r.nhash[nidx] = r.nhash[l] |
| r.nhash = r.nhash[:l] |
| |
| // update the map |
| delete(r.nodes, node) |
| moved := r.nstr[nidx] |
| r.nodes[moved] = nidx |
| } |
| |
| func xorshiftMult64(x uint64) uint64 { |
| x ^= x >> 12 // a |
| x ^= x << 25 // b |
| x ^= x >> 27 // c |
| return x * 2685821657736338717 |
| } |