github.com/go-board/x-go@v0.1.2-0.20220610024734-db1323f6cb15/xhash/ring/hash.go (about) 1 package ring 2 3 import ( 4 "hash/crc32" 5 "sort" 6 "strconv" 7 ) 8 9 type Hash func(data []byte) uint32 10 11 type Map struct { 12 hash Hash 13 replicas int 14 keys []int // Sorted 15 hashMap map[int]string 16 } 17 18 // New returns a new ConsistentHashMap 19 func New(replicas int, fn Hash) *Map { 20 m := &Map{ 21 replicas: replicas, 22 hash: fn, 23 hashMap: make(map[int]string), 24 } 25 if m.hash == nil { 26 m.hash = crc32.ChecksumIEEE 27 } 28 return m 29 } 30 31 // IsEmpty returns true if there are no items available. 32 func (m *Map) IsEmpty() bool { 33 return len(m.keys) == 0 34 } 35 36 // Add adds some nodes to the hash. 37 func (m *Map) Add(nodes ...string) { 38 for _, key := range nodes { 39 for i := 0; i < m.replicas; i++ { 40 hash := int(m.hash([]byte(strconv.Itoa(i) + key))) 41 m.keys = append(m.keys, hash) 42 m.hashMap[hash] = key 43 } 44 } 45 sort.Ints(m.keys) 46 } 47 48 // Remove nodes from hash 49 func (m *Map) Remove(nodes ...string) { 50 for _, key := range nodes { 51 for i := 0; i < m.replicas; i++ { 52 hash := int(m.hash([]byte(strconv.Itoa(i) + key))) 53 idx := sort.SearchInts(m.keys, hash) 54 if idx >= 0 { 55 copy(m.keys[idx:], m.keys[idx+1:]) 56 m.keys = m.keys[:len(m.keys)-1] 57 } 58 delete(m.hashMap, hash) 59 } 60 } 61 } 62 63 // Get gets the closest item in the hash to the provided key. 64 func (m *Map) Get(key string) string { 65 if m.IsEmpty() { 66 return "" 67 } 68 69 hash := int(m.hash([]byte(key))) 70 71 // Binary search for appropriate replica. 72 idx := sort.Search(len(m.keys), func(i int) bool { return m.keys[i] >= hash }) 73 74 // Means we have cycled back to the first replica. 75 if idx == len(m.keys) { 76 idx = 0 77 } 78 79 return m.hashMap[m.keys[idx]] 80 }