github.com/aacfactory/rings@v1.1.2/hashed.go (about)

     1  package rings
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/cespare/xxhash/v2"
     6  	"sort"
     7  	"sync"
     8  )
     9  
    10  const (
    11  	maxEntries = uint64(256)
    12  )
    13  
    14  type HashRingEntry[E Entry] struct {
    15  	Entry  E
    16  	Active bool
    17  	Low    uint64
    18  	High   uint64
    19  }
    20  
    21  func (entry *HashRingEntry[E]) String() (s string) {
    22  	active := "F"
    23  	if entry.Active {
    24  		active = "T"
    25  	}
    26  	s = fmt.Sprintf("{%s:[%d, %d) (%s)}", entry.Entry.Key(), entry.Low, entry.High, active)
    27  	return
    28  }
    29  
    30  func (entry *HashRingEntry[E]) Less(o *HashRingEntry[E]) bool {
    31  	if entry.High < o.High {
    32  		return true
    33  	}
    34  	return entry.High == o.High && entry.Low < o.Low
    35  }
    36  
    37  func (entry *HashRingEntry[E]) RangeSize() uint64 {
    38  	return entry.High - entry.Low
    39  }
    40  
    41  type RangeSizeSortedHashRingEntries[E Entry] []*HashRingEntry[E]
    42  
    43  func (entries RangeSizeSortedHashRingEntries[E]) Len() int {
    44  	return len(entries)
    45  }
    46  
    47  func (entries RangeSizeSortedHashRingEntries[E]) Less(i, j int) bool {
    48  	return entries[i].RangeSize() < entries[j].RangeSize() && entries[j].Active
    49  }
    50  
    51  func (entries RangeSizeSortedHashRingEntries[E]) Swap(i, j int) {
    52  	entries[i], entries[j] = entries[j], entries[i]
    53  	return
    54  }
    55  
    56  type HashRingEntries[E Entry] []*HashRingEntry[E]
    57  
    58  func (entries HashRingEntries[E]) Len() int {
    59  	return len(entries)
    60  }
    61  
    62  func (entries HashRingEntries[E]) Less(i, j int) bool {
    63  	return entries[i].Less(entries[j])
    64  }
    65  
    66  func (entries HashRingEntries[E]) Swap(i, j int) {
    67  	entries[i], entries[j] = entries[j], entries[i]
    68  	return
    69  }
    70  
    71  func (entries HashRingEntries[E]) BiggestRangeAndActiveOne() (entry *HashRingEntry[E]) {
    72  	sorted := RangeSizeSortedHashRingEntries[E](entries)
    73  	sort.Sort(sorted)
    74  	target := sorted[sorted.Len()-1]
    75  	sort.Sort(entries)
    76  	if !target.Active {
    77  		return
    78  	}
    79  	entry = target
    80  	return
    81  }
    82  
    83  func (entries HashRingEntries[E]) Get(n uint64) (entry *HashRingEntry[E], has bool) {
    84  	idx := -1
    85  	for i, hashed := range entries {
    86  		if hashed.Low <= n && hashed.High > n {
    87  			entry = hashed
    88  			idx = i
    89  			break
    90  		}
    91  	}
    92  	entry = entries[idx]
    93  	if entry.Active {
    94  		has = true
    95  		return
    96  	}
    97  	for {
    98  		idx--
    99  		if idx < 0 {
   100  			break
   101  		}
   102  		entry = entries[idx]
   103  		if entry.Active {
   104  			has = true
   105  			return
   106  		}
   107  	}
   108  	return
   109  }
   110  
   111  func NewHashed[E Entry](entries ...E) (v *HashRing[E]) {
   112  	hashedEntries := make([]*HashRingEntry[E], 0, 1)
   113  	if entries != nil && len(entries) > 0 {
   114  		for _, entry := range entries {
   115  			hashedEntries = append(hashedEntries, &HashRingEntry[E]{
   116  				Entry:  entry,
   117  				Active: true,
   118  				Low:    0,
   119  				High:   0,
   120  			})
   121  		}
   122  		span := maxEntries / uint64(len(hashedEntries))
   123  		for i, entry := range hashedEntries {
   124  			entry.Low = span * uint64(i)
   125  			entry.High = span * uint64(i+1)
   126  		}
   127  		hashedEntries[len(entries)-1].High = maxEntries
   128  	}
   129  	v = &HashRing[E]{
   130  		locker:  sync.RWMutex{},
   131  		entries: hashedEntries,
   132  	}
   133  	return
   134  }
   135  
   136  type HashRing[E Entry] struct {
   137  	locker  sync.RWMutex
   138  	entries HashRingEntries[E]
   139  }
   140  
   141  func (r *HashRing[E]) Get(key []byte) (entry E, has bool) {
   142  	r.locker.RLock()
   143  	idx := xxhash.Sum64(key) % maxEntries
   144  	hashed, hasHashed := r.entries.Get(idx)
   145  	if !hasHashed {
   146  		r.locker.RUnlock()
   147  		return
   148  	}
   149  	entry = hashed.Entry
   150  	has = true
   151  	r.locker.RUnlock()
   152  	return
   153  }
   154  
   155  func (r *HashRing[E]) Add(entry E) (prevActive E, cLow uint64, cHigh uint64, active func(), cancel func(), ok bool) {
   156  	r.locker.Lock()
   157  	defer r.locker.Unlock()
   158  	if uint64(r.entries.Len()) >= maxEntries {
   159  		return
   160  	}
   161  	var prevActiveHashed *HashRingEntry[E]
   162  	hashed := &HashRingEntry[E]{
   163  		Entry:  entry,
   164  		Active: false,
   165  		Low:    0,
   166  		High:   maxEntries,
   167  	}
   168  	if r.entries.Len() == 0 {
   169  		r.entries = append(r.entries, hashed)
   170  	} else {
   171  		prevActiveHashed = r.entries.BiggestRangeAndActiveOne()
   172  		if prevActiveHashed == nil {
   173  			return
   174  		}
   175  		hashed.Low = (prevActiveHashed.High-prevActiveHashed.Low)/2 + prevActiveHashed.Low
   176  		hashed.High = prevActiveHashed.High
   177  		prevActiveHashed.High = hashed.Low
   178  		prevActive = prevActiveHashed.Entry
   179  		r.entries = append(r.entries, hashed)
   180  		sort.Sort(r.entries)
   181  	}
   182  	cLow = hashed.Low
   183  	cHigh = hashed.High
   184  	active = func() {
   185  		r.locker.Lock()
   186  		hashed.Active = true
   187  		r.locker.Unlock()
   188  	}
   189  	cancel = func() {
   190  		r.locker.Lock()
   191  		if prevActiveHashed != nil {
   192  			prevActiveHashed.High = hashed.High
   193  		}
   194  		entries := make([]*HashRingEntry[E], 0, r.entries.Len()-1)
   195  		for _, hashedEntry := range r.entries {
   196  			if hashedEntry.Entry.Key() == entry.Key() {
   197  				continue
   198  			}
   199  			entries = append(entries, hashedEntry)
   200  		}
   201  		r.entries = entries
   202  		sort.Sort(r.entries)
   203  		r.locker.Unlock()
   204  	}
   205  	ok = true
   206  	return
   207  }
   208  
   209  func (r *HashRing[E]) AddDeclared(entry E, low uint64, high uint64) (ok bool) {
   210  	r.locker.Lock()
   211  	defer r.locker.Unlock()
   212  	if uint64(r.entries.Len()) >= maxEntries {
   213  		return
   214  	}
   215  	hashed := &HashRingEntry[E]{
   216  		Entry:  entry,
   217  		Active: true,
   218  		Low:    low,
   219  		High:   high,
   220  	}
   221  	if r.entries.Len() == 0 {
   222  		r.entries = append(r.entries, hashed)
   223  	} else {
   224  		for _, e := range r.entries {
   225  			if intersect(e.Low, e.High, low, high) {
   226  				return
   227  			}
   228  		}
   229  		r.entries = append(r.entries, hashed)
   230  		sort.Sort(r.entries)
   231  	}
   232  	ok = true
   233  	return
   234  }
   235  
   236  func (r *HashRing[E]) Size() (n int) {
   237  	n = len(r.entries)
   238  	return
   239  }
   240  
   241  func (r *HashRing[E]) State(key string) (active bool, low uint64, high uint64, has bool) {
   242  	for _, entry := range r.entries {
   243  		if entry.Entry.Key() == key {
   244  			active = entry.Active
   245  			low = entry.Low
   246  			high = entry.High
   247  			has = true
   248  			return
   249  		}
   250  	}
   251  	return
   252  }
   253  
   254  func (r *HashRing[E]) States(fn func(key string, active bool, low uint64, high uint64) bool) {
   255  	for _, entry := range r.entries {
   256  		if !fn(entry.Entry.Key(), entry.Active, entry.Low, entry.High) {
   257  			break
   258  		}
   259  	}
   260  	return
   261  }
   262  
   263  func (r *HashRing[E]) String() (s string) {
   264  	for _, entry := range r.entries {
   265  		s = s + ", " + entry.String()
   266  	}
   267  	if s != "" {
   268  		s = s[2:]
   269  	}
   270  	s = "[" + s + "]"
   271  	return
   272  }
   273  
   274  func intersect(sLow uint64, sHigh uint64, tLow uint64, tHigh uint64) (ok bool) {
   275  	for s := sLow; s < sHigh; s++ {
   276  		for t := tLow; t < tHigh; t++ {
   277  			if s == t {
   278  				ok = true
   279  				return
   280  			}
   281  		}
   282  	}
   283  	return
   284  }