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

     1  // Copyright 2023-2024 Phus Lu. All rights reserved.
     2  
     3  package lru
     4  
     5  import (
     6  	"sync"
     7  	"unsafe"
     8  )
     9  
    10  // bytesnode is a list of bytes node, storing key-value pairs and related information
    11  type bytesnode struct {
    12  	key   []byte
    13  	next  uint32
    14  	prev  uint32
    15  	value []byte
    16  }
    17  
    18  type bytesbucket struct {
    19  	hdib  uint32 // bitfield { hash:24 dib:8 }
    20  	index uint32 // node index
    21  }
    22  
    23  // bytesshard is a LRU partition contains a list and a hash table.
    24  type bytesshard struct {
    25  	mu sync.Mutex
    26  
    27  	// the hash table, with 20% extra space than the list for fewer conflicts.
    28  	table_buckets []uint64 // []bytesbucket
    29  	table_mask    uint32
    30  	table_length  uint32
    31  
    32  	// the list of nodes
    33  	list []bytesnode
    34  
    35  	// stats
    36  	stats_getcalls uint64
    37  	stats_setcalls uint64
    38  	stats_misses   uint64
    39  
    40  	// padding
    41  	_ [40]byte
    42  }
    43  
    44  func (s *bytesshard) Init(size uint32) {
    45  	s.list_Init(size)
    46  	s.table_Init(size)
    47  }
    48  
    49  func (s *bytesshard) Get(hash uint32, key []byte) (value []byte, ok bool) {
    50  	s.mu.Lock()
    51  
    52  	s.stats_getcalls++
    53  
    54  	if index, exists := s.table_Get(hash, key); exists {
    55  		s.list_MoveToFront(index)
    56  		// value = s.list[index].value
    57  		value = (*bytesnode)(unsafe.Add(unsafe.Pointer(&s.list[0]), uintptr(index)*unsafe.Sizeof(s.list[0]))).value
    58  		ok = true
    59  	} else {
    60  		s.stats_misses++
    61  	}
    62  
    63  	s.mu.Unlock()
    64  
    65  	return
    66  }
    67  
    68  func (s *bytesshard) Peek(hash uint32, key []byte) (value []byte, ok bool) {
    69  	s.mu.Lock()
    70  
    71  	if index, exists := s.table_Get(hash, key); exists {
    72  		value = s.list[index].value
    73  		ok = true
    74  	}
    75  
    76  	s.mu.Unlock()
    77  
    78  	return
    79  }
    80  
    81  func (s *bytesshard) SetIfAbsent(hash uint32, key []byte, value []byte) (prev []byte, replaced bool) {
    82  	s.mu.Lock()
    83  
    84  	if index, exists := s.table_Get(hash, key); exists {
    85  		prev = s.list[index].value
    86  		s.mu.Unlock()
    87  		return
    88  	}
    89  
    90  	s.stats_setcalls++
    91  
    92  	// index := s.list_Back()
    93  	// node := &s.list[index]
    94  	index := s.list[0].prev
    95  	node := (*bytesnode)(unsafe.Add(unsafe.Pointer(&s.list[0]), uintptr(index)*unsafe.Sizeof(s.list[0])))
    96  	evictedValue := node.value
    97  	s.table_Delete(uint32(wyhash_HashBytes(node.key, 0)), node.key)
    98  
    99  	node.key = key
   100  	node.value = value
   101  	s.table_Set(hash, key, index)
   102  	s.list_MoveToFront(index)
   103  	prev = evictedValue
   104  
   105  	s.mu.Unlock()
   106  	return
   107  }
   108  
   109  func (s *bytesshard) Set(hash uint32, key []byte, value []byte) (prev []byte, replaced bool) {
   110  	s.mu.Lock()
   111  
   112  	s.stats_setcalls++
   113  
   114  	if index, exists := s.table_Get(hash, key); exists {
   115  		// node := &s.list[index]
   116  		node := (*bytesnode)(unsafe.Add(unsafe.Pointer(&s.list[0]), uintptr(index)*unsafe.Sizeof(s.list[0])))
   117  		previousValue := node.value
   118  		s.list_MoveToFront(index)
   119  		node.value = value
   120  		prev = previousValue
   121  		replaced = true
   122  
   123  		s.mu.Unlock()
   124  		return
   125  	}
   126  
   127  	// index := s.list_Back()
   128  	// node := &s.list[index]
   129  	index := s.list[0].prev
   130  	node := (*bytesnode)(unsafe.Add(unsafe.Pointer(&s.list[0]), uintptr(index)*unsafe.Sizeof(s.list[0])))
   131  	evictedValue := node.value
   132  	s.table_Delete(uint32(wyhash_HashBytes(node.key, 0)), node.key)
   133  
   134  	node.key = key
   135  	node.value = value
   136  	s.table_Set(hash, key, index)
   137  	s.list_MoveToFront(index)
   138  	prev = evictedValue
   139  
   140  	s.mu.Unlock()
   141  	return
   142  }
   143  
   144  func (s *bytesshard) Delete(hash uint32, key []byte) (v []byte) {
   145  	s.mu.Lock()
   146  
   147  	if index, exists := s.table_Get(hash, key); exists {
   148  		node := &s.list[index]
   149  		value := node.value
   150  		s.list_MoveToBack(index)
   151  		node.value = v
   152  		s.table_Delete(hash, key)
   153  		v = value
   154  	}
   155  
   156  	s.mu.Unlock()
   157  
   158  	return
   159  }
   160  
   161  func (s *bytesshard) Len() (n uint32) {
   162  	s.mu.Lock()
   163  	// inlining s.table_Len()
   164  	n = s.table_length
   165  	s.mu.Unlock()
   166  
   167  	return
   168  }
   169  
   170  func (s *bytesshard) AppendKeys(dst [][]byte) [][]byte {
   171  	s.mu.Lock()
   172  	for _, bucket := range s.table_buckets {
   173  		b := (*bytesbucket)(unsafe.Pointer(&bucket))
   174  		if b.index == 0 {
   175  			continue
   176  		}
   177  		dst = append(dst, s.list[b.index].key)
   178  	}
   179  	s.mu.Unlock()
   180  
   181  	return dst
   182  }