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  }