github.com/fufuok/balancer@v1.0.0/internal/doublejump/doublejump.go (about)

     1  // Package doublejump provides a revamped Google's jump consistent hash.
     2  package doublejump
     3  
     4  import (
     5  	"math/rand"
     6  
     7  	"github.com/fufuok/balancer/internal/go-jump"
     8  )
     9  
    10  type looseHolder struct {
    11  	a []interface{}
    12  	m map[interface{}]int
    13  	f []int
    14  }
    15  
    16  func (l *looseHolder) add(obj interface{}) {
    17  	if _, ok := l.m[obj]; ok {
    18  		return
    19  	}
    20  
    21  	if nf := len(l.f); nf == 0 {
    22  		l.a = append(l.a, obj)
    23  		l.m[obj] = len(l.a) - 1
    24  	} else {
    25  		idx := l.f[nf-1]
    26  		l.f = l.f[:nf-1]
    27  		l.a[idx] = obj
    28  		l.m[obj] = idx
    29  	}
    30  }
    31  
    32  func (l *looseHolder) remove(obj interface{}) {
    33  	if idx, ok := l.m[obj]; ok {
    34  		l.f = append(l.f, idx)
    35  		l.a[idx] = nil
    36  		delete(l.m, obj)
    37  	}
    38  }
    39  
    40  func (l *looseHolder) get(key uint64) interface{} {
    41  	na := len(l.a)
    42  	if na == 0 {
    43  		return nil
    44  	}
    45  
    46  	h := jump.Hash(key, na)
    47  	return l.a[h]
    48  }
    49  
    50  func (l *looseHolder) shrink() {
    51  	if len(l.f) == 0 {
    52  		return
    53  	}
    54  
    55  	var a []interface{}
    56  	for _, obj := range l.a {
    57  		if obj != nil {
    58  			a = append(a, obj)
    59  			l.m[obj] = len(a) - 1
    60  		}
    61  	}
    62  	l.a = a
    63  	l.f = nil
    64  }
    65  
    66  type compactHolder struct {
    67  	a []interface{}
    68  	m map[interface{}]int
    69  }
    70  
    71  func (c *compactHolder) add(obj interface{}) {
    72  	if _, ok := c.m[obj]; ok {
    73  		return
    74  	}
    75  
    76  	c.a = append(c.a, obj)
    77  	c.m[obj] = len(c.a) - 1
    78  }
    79  
    80  func (c *compactHolder) shrink(a []interface{}) {
    81  	for i, obj := range a {
    82  		c.a[i] = obj
    83  		c.m[obj] = i
    84  	}
    85  }
    86  
    87  func (c *compactHolder) remove(obj interface{}) {
    88  	if idx, ok := c.m[obj]; ok {
    89  		n := len(c.a)
    90  		c.a[idx] = c.a[n-1]
    91  		c.m[c.a[idx]] = idx
    92  		c.a[n-1] = nil
    93  		c.a = c.a[:n-1]
    94  		delete(c.m, obj)
    95  	}
    96  }
    97  
    98  func (c *compactHolder) get(key uint64) interface{} {
    99  	na := len(c.a)
   100  	if na == 0 {
   101  		return nil
   102  	}
   103  
   104  	h := jump.Hash(key*0xc6a4a7935bd1e995, na)
   105  	return c.a[h]
   106  }
   107  
   108  // Hash is a revamped Google's jump consistent hash. It overcomes the shortcoming of the
   109  // original implementation - not being able to remove nodes.
   110  type Hash struct {
   111  	loose   looseHolder
   112  	compact compactHolder
   113  }
   114  
   115  // NewHash creates a new doublejump hash instance, which does NOT threadsafe.
   116  func NewHash() *Hash {
   117  	hash := &Hash{}
   118  	hash.loose.m = make(map[interface{}]int)
   119  	hash.compact.m = make(map[interface{}]int)
   120  	return hash
   121  }
   122  
   123  // Add adds an object to the hash.
   124  func (h *Hash) Add(obj interface{}) {
   125  	if obj == nil {
   126  		return
   127  	}
   128  
   129  	h.loose.add(obj)
   130  	h.compact.add(obj)
   131  }
   132  
   133  // Remove removes an object from the hash.
   134  func (h *Hash) Remove(obj interface{}) {
   135  	if obj == nil {
   136  		return
   137  	}
   138  
   139  	h.loose.remove(obj)
   140  	h.compact.remove(obj)
   141  }
   142  
   143  // Len returns the number of objects in the hash.
   144  func (h *Hash) Len() int {
   145  	return len(h.compact.a)
   146  }
   147  
   148  // LooseLen returns the size of the inner loose object holder.
   149  func (h *Hash) LooseLen() int {
   150  	return len(h.loose.a)
   151  }
   152  
   153  // Shrink removes all empty slots from the hash.
   154  func (h *Hash) Shrink() {
   155  	h.loose.shrink()
   156  	h.compact.shrink(h.loose.a)
   157  }
   158  
   159  // Get returns an object according to the key provided.
   160  func (h *Hash) Get(key uint64) interface{} {
   161  	obj := h.loose.get(key)
   162  	switch obj {
   163  	case nil:
   164  		return h.compact.get(key)
   165  	default:
   166  		return obj
   167  	}
   168  }
   169  
   170  // All returns all the objects in this Hash.
   171  func (h *Hash) All() []interface{} {
   172  	n := len(h.compact.a)
   173  	if n == 0 {
   174  		return nil
   175  	}
   176  	all := make([]interface{}, n)
   177  	copy(all, h.compact.a)
   178  	return all
   179  }
   180  
   181  // Random returns a random object.
   182  func (h *Hash) Random() interface{} {
   183  	if n := len(h.compact.a); n > 0 {
   184  		idx := rand.Intn(n)
   185  		return h.compact.a[idx]
   186  	}
   187  	return nil
   188  }