github.com/leslie-fei/fastcache@v0.0.0-20240520092641-b7a9eb05711f/block.go (about) 1 package fastcache 2 3 import ( 4 "errors" 5 "math" 6 "unsafe" 7 ) 8 9 var ( 10 ErrIndexOutOfRange = errors.New("index out of range") 11 ErrLRUListEmpty = errors.New("LRU list is empty") 12 ) 13 14 const FreeListLen = 25 // 1 2 4 8 ... 16M, max free DataNode size = 16M 15 var SmallFreeListIndex = dataSizeToIndex(64 * KB) 16 17 type lruAndFreeContainer struct { 18 freeLists [FreeListLen]blockFreeList 19 lruLists [FreeListLen]list 20 } 21 22 func (b *lruAndFreeContainer) Init(base uintptr) { 23 size := 1 24 for i := 0; i < len(b.freeLists); i++ { 25 freeList := &b.freeLists[i] 26 freeList.Size = uint64(size) 27 freeList.Index = uint8(i) 28 29 lruList := &b.lruLists[i] 30 lruList.Init(base) 31 32 size *= 2 33 } 34 } 35 36 func (b *lruAndFreeContainer) Get(dataSize uint64) (*blockFreeList, error) { 37 if dataSize == 0 { 38 return nil, errors.New("data size is zero") 39 } 40 41 idx := dataSizeToIndex(dataSize) 42 if idx > len(b.freeLists)-1 { 43 return nil, ErrIndexOutOfRange 44 } 45 46 return &b.freeLists[idx], nil 47 } 48 49 func (b *lruAndFreeContainer) GetIndex(idx uint8) *blockFreeList { 50 return &b.freeLists[idx] 51 } 52 53 func (b *lruAndFreeContainer) MaxSize() uint64 { 54 return b.freeLists[len(b.freeLists)-1].Size 55 } 56 57 func (b *lruAndFreeContainer) Len() int { 58 return len(b.freeLists) 59 } 60 61 func (b *lruAndFreeContainer) Alloc(allocator Allocator, dataSize uint64) (*DataNode, error) { 62 freeList, err := b.Get(dataSize) 63 if err != nil { 64 return nil, err 65 } 66 67 // 一个节点需要的字节数等于链表头长度+定长数据长度 68 fixedSize := freeList.Size 69 nodeSize := uint64(sizeOfDataNode) + fixedSize 70 71 if freeList.Len == 0 { 72 // if alloc size less than PageSize will alloc PageSize, other alloc nodeSize 73 allocSize := uint64(PageSize) 74 if nodeSize > PageSize { 75 allocSize = nodeSize 76 } 77 _, offset, err := allocator.Alloc(allocSize) 78 if err != nil { 79 return nil, err 80 } 81 // 设置第一个链表节点的offset 82 nodeLen := allocSize / nodeSize 83 head := freeList.First(allocator.Base()) 84 // 头插法 85 for i := 0; i < int(nodeLen); i++ { 86 ptr := unsafe.Pointer(allocator.Base() + uintptr(offset)) 87 node := (*DataNode)(ptr) 88 node.Reset() 89 // 填写数据的指针位置 90 node.FreeBlockIndex = freeList.Index 91 if head == nil { 92 head = node 93 } else { 94 // 头插, 把当前的head, 前面插入node节点 95 next := head 96 node.Next = next.Offset(allocator.Base()) 97 head = node 98 } 99 offset += nodeSize 100 } 101 freeList.Len += uint32(nodeLen) 102 if head != nil { 103 freeList.Head = head.Offset(allocator.Base()) 104 } 105 } 106 107 // 把第一个链表节点取出 108 node := freeList.First(allocator.Base()) 109 freeList.Head = node.Next 110 freeList.Len-- 111 // 断开与这个链表的关联, 变成一个独立的node 112 node.Next = 0 113 114 return node, nil 115 } 116 117 func (b *lruAndFreeContainer) MoveToFront(base uintptr, node *DataNode, lruNode *listNode) { 118 lruList := &b.lruLists[node.FreeBlockIndex] 119 lruList.MoveToFront(base, lruNode) 120 } 121 122 func (b *lruAndFreeContainer) Free(base uintptr, node *DataNode, lruNode *listNode) { 123 // remove LRU list 124 lruList := &b.lruLists[node.FreeBlockIndex] 125 lruList.Remove(base, lruNode) 126 127 // insert to free list 128 freeList := b.GetIndex(node.FreeBlockIndex) 129 node.Reset() 130 next := freeList.Head 131 node.Next = next 132 freeList.Head = uint64(uintptr(unsafe.Pointer(node)) - base) 133 } 134 135 func (b *lruAndFreeContainer) Evict(allocator *globalAllocator, size uint64) error { 136 index := dataSizeToIndex(size) 137 lruList := &b.lruLists[index] 138 if lruList.Len() == 0 { 139 return ErrLRUListEmpty 140 } 141 oldest := lruList.Back(allocator.Base()) 142 lruList.Remove(allocator.Base(), oldest) 143 return nil 144 } 145 146 type blockFreeList struct { 147 Head uint64 // head of data DataNode 148 Len uint32 // data len 149 Size uint64 // block bytes size 150 Index uint8 151 } 152 153 func (bl *blockFreeList) First(base uintptr) *DataNode { 154 if bl.Len == 0 { 155 return nil 156 } 157 return (*DataNode)(unsafe.Pointer(base + uintptr(bl.Head))) 158 } 159 160 func dataSizeToIndex(size uint64) int { 161 v := math.Log2(float64(size)) 162 return int(math.Ceil(v)) 163 }