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  }