github.com/zuoyebang/bitalosdb@v1.1.1-0.20240516111551-79a8c4d8ce20/internal/cache/lrucache/robin_hood.go (about)

     1  // Copyright 2021 The Bitalosdb author(hustxrb@163.com) and other contributors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package lrucache
    16  
    17  import (
    18  	"fmt"
    19  	"math/bits"
    20  	"os"
    21  	"runtime/debug"
    22  	"strings"
    23  	"time"
    24  	"unsafe"
    25  
    26  	"github.com/zuoyebang/bitalosdb/internal/invariants"
    27  	"github.com/zuoyebang/bitalosdb/internal/manual"
    28  )
    29  
    30  var hashSeed = uint64(time.Now().UnixNano())
    31  
    32  func robinHoodHash(k key, shift uint32) uint32 {
    33  	const m = 11400714819323198485
    34  	h := hashSeed
    35  	h ^= k.id * m
    36  	h ^= k.offset * m
    37  	return uint32(h >> shift)
    38  }
    39  
    40  type robinHoodEntry struct {
    41  	key   key
    42  	value *entry
    43  	dist  uint32
    44  }
    45  
    46  type robinHoodEntries struct {
    47  	ptr unsafe.Pointer
    48  	len uint32
    49  }
    50  
    51  func newRobinHoodEntries(n uint32) robinHoodEntries {
    52  	size := uintptr(n) * unsafe.Sizeof(robinHoodEntry{})
    53  	return robinHoodEntries{
    54  		ptr: unsafe.Pointer(&(manual.New(int(size)))[0]),
    55  		len: n,
    56  	}
    57  }
    58  
    59  func (e robinHoodEntries) at(i uint32) *robinHoodEntry {
    60  	return (*robinHoodEntry)(unsafe.Pointer(uintptr(e.ptr) +
    61  		uintptr(i)*unsafe.Sizeof(robinHoodEntry{})))
    62  }
    63  
    64  func (e robinHoodEntries) free() {
    65  	size := uintptr(e.len) * unsafe.Sizeof(robinHoodEntry{})
    66  	buf := (*[manual.MaxArrayLen]byte)(e.ptr)[:size:size]
    67  	manual.Free(buf)
    68  }
    69  
    70  type robinHoodMap struct {
    71  	entries robinHoodEntries
    72  	size    uint32
    73  	shift   uint32
    74  	count   uint32
    75  	maxDist uint32
    76  }
    77  
    78  func maxDistForSize(size uint32) uint32 {
    79  	desired := uint32(bits.Len32(size))
    80  	if desired < 4 {
    81  		desired = 4
    82  	}
    83  	return desired
    84  }
    85  
    86  func newRobinHoodMap(initialCapacity int) *robinHoodMap {
    87  	m := &robinHoodMap{}
    88  	m.init(initialCapacity)
    89  
    90  	invariants.SetFinalizer(m, func(obj interface{}) {
    91  		m := obj.(*robinHoodMap)
    92  		if m.entries.ptr != nil {
    93  			fmt.Fprintf(os.Stderr, "%p: robin-hood map not freed\n", m)
    94  			os.Exit(1)
    95  		}
    96  	})
    97  	return m
    98  }
    99  
   100  func (m *robinHoodMap) init(initialCapacity int) {
   101  	if initialCapacity < 1 {
   102  		initialCapacity = 1
   103  	}
   104  	targetSize := 1 << (uint(bits.Len(uint(2*initialCapacity-1))) - 1)
   105  	m.rehash(uint32(targetSize))
   106  }
   107  
   108  func (m *robinHoodMap) free() {
   109  	if m.entries.ptr != nil {
   110  		m.entries.free()
   111  		m.entries.ptr = nil
   112  	}
   113  }
   114  
   115  func (m *robinHoodMap) rehash(size uint32) {
   116  	oldEntries := m.entries
   117  
   118  	m.size = size
   119  	m.shift = uint32(64 - bits.Len32(m.size-1))
   120  	m.maxDist = maxDistForSize(size)
   121  	m.entries = newRobinHoodEntries(size + m.maxDist)
   122  	m.count = 0
   123  
   124  	for i := uint32(0); i < oldEntries.len; i++ {
   125  		e := oldEntries.at(i)
   126  		if e.value != nil {
   127  			m.Put(e.key, e.value)
   128  		}
   129  	}
   130  
   131  	if oldEntries.ptr != nil {
   132  		oldEntries.free()
   133  	}
   134  }
   135  
   136  func (m *robinHoodMap) findByValue(v *entry) *robinHoodEntry {
   137  	for i := uint32(0); i < m.entries.len; i++ {
   138  		e := m.entries.at(i)
   139  		if e.value == v {
   140  			return e
   141  		}
   142  	}
   143  	return nil
   144  }
   145  
   146  func (m *robinHoodMap) Count() int {
   147  	return int(m.count)
   148  }
   149  
   150  func (m *robinHoodMap) Put(k key, v *entry) {
   151  	maybeExists := true
   152  	n := robinHoodEntry{key: k, value: v, dist: 0}
   153  	for i := robinHoodHash(k, m.shift); ; i++ {
   154  		e := m.entries.at(i)
   155  		if maybeExists && k == e.key {
   156  			e.value = n.value
   157  			m.checkEntry(i)
   158  			return
   159  		}
   160  
   161  		if e.value == nil {
   162  			*e = n
   163  			m.count++
   164  			m.checkEntry(i)
   165  			return
   166  		}
   167  
   168  		if e.dist < n.dist {
   169  			n, *e = *e, n
   170  			m.checkEntry(i)
   171  			maybeExists = false
   172  		}
   173  
   174  		n.dist++
   175  
   176  		if n.dist == m.maxDist {
   177  			m.rehash(2 * m.size)
   178  			i = robinHoodHash(n.key, m.shift) - 1
   179  			n.dist = 0
   180  			maybeExists = false
   181  		}
   182  	}
   183  }
   184  
   185  func (m *robinHoodMap) Get(k key) *entry {
   186  	var dist uint32
   187  	for i := robinHoodHash(k, m.shift); ; i++ {
   188  		e := m.entries.at(i)
   189  		if k == e.key {
   190  			return e.value
   191  		}
   192  		if e.dist < dist {
   193  			return nil
   194  		}
   195  		dist++
   196  	}
   197  }
   198  
   199  func (m *robinHoodMap) Delete(k key) {
   200  	var dist uint32
   201  	for i := robinHoodHash(k, m.shift); ; i++ {
   202  		e := m.entries.at(i)
   203  		if k == e.key {
   204  			m.checkEntry(i)
   205  			m.count--
   206  			for j := i + 1; ; j++ {
   207  				t := m.entries.at(j)
   208  				if t.dist == 0 {
   209  					*e = robinHoodEntry{}
   210  					return
   211  				}
   212  				e.key = t.key
   213  				e.value = t.value
   214  				e.dist = t.dist - 1
   215  				e = t
   216  				m.checkEntry(j)
   217  			}
   218  		}
   219  		if dist > e.dist {
   220  			return
   221  		}
   222  		dist++
   223  	}
   224  }
   225  
   226  func (m *robinHoodMap) checkEntry(i uint32) {
   227  	if invariants.Enabled {
   228  		e := m.entries.at(i)
   229  		if e.value != nil {
   230  			pos := robinHoodHash(e.key, m.shift)
   231  			if (uint32(i) - pos) != e.dist {
   232  				fmt.Fprintf(os.Stderr, "%d: invalid dist=%d, expected %d: %s\n%s",
   233  					i, e.dist, uint32(i)-pos, e.key, debug.Stack())
   234  				os.Exit(1)
   235  			}
   236  			if e.dist > m.maxDist {
   237  				fmt.Fprintf(os.Stderr, "%d: invalid dist=%d > maxDist=%d: %s\n%s",
   238  					i, e.dist, m.maxDist, e.key, debug.Stack())
   239  				os.Exit(1)
   240  			}
   241  		}
   242  	}
   243  }
   244  
   245  func (m *robinHoodMap) String() string {
   246  	var buf strings.Builder
   247  	fmt.Fprintf(&buf, "count: %d\n", m.count)
   248  	for i := uint32(0); i < m.entries.len; i++ {
   249  		e := m.entries.at(i)
   250  		if e.value != nil {
   251  			fmt.Fprintf(&buf, "%d: [%s,%p,%d]\n", i, e.key, e.value, e.dist)
   252  		}
   253  	}
   254  	return buf.String()
   255  }