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 }