github.com/phuslu/lru@v1.0.16-0.20240421170520-46288a2fd47c/lru_shard_table.go (about)

     1  // Copyright 2023-2024 Phus Lu. All rights reserved.
     2  // Copyright 2019 Joshua J Baker. All rights reserved.
     3  // Use of this source code is governed by an ISC-style
     4  // license that can be found in the LICENSE file.
     5  
     6  package lru
     7  
     8  import (
     9  	"unsafe"
    10  )
    11  
    12  const (
    13  	loadFactor  = 0.85                      // must be above 50%
    14  	dibBitSize  = 8                         // 0xFF
    15  	hashBitSize = 32 - dibBitSize           // 0xFFFFFF
    16  	maxHash     = ^uint32(0) >> dibBitSize  // max 16777215
    17  	maxDIB      = ^uint32(0) >> hashBitSize // max 255
    18  )
    19  
    20  func (s *lrushard[K, V]) table_Init(size uint32, hasher func(key unsafe.Pointer, seed uintptr) uintptr, seed uintptr) {
    21  	newsize := lruNewTableSize(size)
    22  	if len(s.table_buckets) == 0 {
    23  		s.table_buckets = make([]uint64, newsize)
    24  	}
    25  	s.table_mask = newsize - 1
    26  	s.table_length = 0
    27  	s.table_hasher = hasher
    28  	s.table_seed = seed
    29  }
    30  
    31  func lruNewTableSize(size uint32) (newsize uint32) {
    32  	newsize = nextPowOf2(size)
    33  	if float64(newsize)*loadFactor < float64(size) {
    34  		newsize = nextPowOf2(newsize + 1)
    35  	}
    36  	if newsize < 8 {
    37  		newsize = 8
    38  	}
    39  	return
    40  }
    41  
    42  // Set assigns an index to a key.
    43  // Returns the previous index, or false when no index was assigned.
    44  func (s *lrushard[K, V]) table_Set(hash uint32, key K, index uint32) (prev uint32, ok bool) {
    45  	subhash := hash >> dibBitSize
    46  	hdib := subhash<<dibBitSize | uint32(1)&maxDIB
    47  	mask := s.table_mask
    48  	i := (hdib >> dibBitSize) & mask
    49  	b0 := unsafe.Pointer(&s.table_buckets[0])
    50  	l0 := unsafe.Pointer(&s.list[0])
    51  	for {
    52  		b := (*lrubucket)(unsafe.Add(b0, uintptr(i)*8))
    53  		if b.hdib&maxDIB == 0 {
    54  			b.hdib = hdib
    55  			b.index = index
    56  			s.table_length++
    57  			return
    58  		}
    59  		if hdib>>dibBitSize == b.hdib>>dibBitSize && (*lrunode[K, V])(unsafe.Add(l0, uintptr(b.index)*unsafe.Sizeof(s.list[0]))).key == key {
    60  			prev = b.index
    61  			b.hdib = hdib
    62  			b.index = index
    63  			ok = true
    64  			return
    65  		}
    66  		if b.hdib&maxDIB < hdib&maxDIB {
    67  			hdib, b.hdib = b.hdib, hdib
    68  			index, b.index = b.index, index
    69  		}
    70  		i = (i + 1) & mask
    71  		hdib = hdib>>dibBitSize<<dibBitSize | (hdib&maxDIB+1)&maxDIB
    72  	}
    73  }
    74  
    75  // table_Get returns an index for a key.
    76  // Returns false when no index has been assign for key.
    77  func (s *lrushard[K, V]) table_Get(hash uint32, key K) (index uint32, ok bool) {
    78  	subhash := hash >> dibBitSize
    79  	mask := s.table_mask
    80  	i := subhash & mask
    81  	b0 := unsafe.Pointer(&s.table_buckets[0])
    82  	l0 := unsafe.Pointer(&s.list[0])
    83  	for {
    84  		b := (*lrubucket)(unsafe.Add(b0, uintptr(i)*8))
    85  		if b.hdib&maxDIB == 0 {
    86  			return
    87  		}
    88  		if b.hdib>>dibBitSize == subhash && (*lrunode[K, V])(unsafe.Add(l0, uintptr(b.index)*unsafe.Sizeof(s.list[0]))).key == key {
    89  			return b.index, true
    90  		}
    91  		i = (i + 1) & mask
    92  	}
    93  }
    94  
    95  // table_Delete deletes an index for a key.
    96  // Returns the deleted index, or false when no index was assigned.
    97  func (s *lrushard[K, V]) table_Delete(hash uint32, key K) (index uint32, ok bool) {
    98  	subhash := hash >> dibBitSize
    99  	mask := s.table_mask
   100  	i := subhash & mask
   101  	b0 := unsafe.Pointer(&s.table_buckets[0])
   102  	l0 := unsafe.Pointer(&s.list[0])
   103  	for {
   104  		b := (*lrubucket)(unsafe.Add(b0, uintptr(i)*8))
   105  		if b.hdib&maxDIB == 0 {
   106  			return
   107  		}
   108  		if b.hdib>>dibBitSize == subhash && (*lrunode[K, V])(unsafe.Add(l0, uintptr(b.index)*unsafe.Sizeof(s.list[0]))).key == key {
   109  			old := b.index
   110  			s.table_delete(i)
   111  			return old, true
   112  		}
   113  		i = (i + 1) & mask
   114  	}
   115  }
   116  
   117  func (s *lrushard[K, V]) table_delete(i uint32) {
   118  	mask := s.table_mask
   119  	b0 := unsafe.Pointer(&s.table_buckets[0])
   120  	bi := (*lrubucket)(unsafe.Add(b0, uintptr(i)*8))
   121  	bi.hdib = bi.hdib>>dibBitSize<<dibBitSize | uint32(0)&maxDIB
   122  	for {
   123  		pi := i
   124  		i = (i + 1) & mask
   125  		bpi := (*lrubucket)(unsafe.Add(b0, uintptr(pi)*8))
   126  		bi = (*lrubucket)(unsafe.Add(b0, uintptr(i)*8))
   127  		if bi.hdib&maxDIB <= 1 {
   128  			bpi.index = 0
   129  			bpi.hdib = 0
   130  			break
   131  		}
   132  		bpi.index = bi.index
   133  		bpi.hdib = bi.hdib>>dibBitSize<<dibBitSize | (bi.hdib&maxDIB-1)&maxDIB
   134  	}
   135  	s.table_length--
   136  }