github.com/sohaha/zlsgo@v1.7.13-0.20240501141223-10dd1a906f76/zcache/lru.go (about)

     1  package zcache
     2  
     3  import (
     4  	"sync"
     5  	"time"
     6  	"unsafe"
     7  
     8  	"github.com/sohaha/zlsgo/ztime"
     9  	"golang.org/x/sync/singleflight"
    10  )
    11  
    12  // FastCache concurrent LRU cache structure
    13  type FastCache struct {
    14  	gsf        singleflight.Group
    15  	callback   handler
    16  	locks      []sync.Mutex
    17  	insts      [][2]*lruCache
    18  	expiration time.Duration
    19  	mask       int32
    20  }
    21  
    22  type Options struct {
    23  	Callback   func(ActionKind, string, uintptr)
    24  	Expiration time.Duration
    25  	Bucket     uint16
    26  	Cap        uint16
    27  	LRU2Cap    uint16
    28  }
    29  
    30  // NewFast Fast LRU cache
    31  func NewFast(opt ...func(o *Options)) *FastCache {
    32  	o := Options{
    33  		Cap:    1 << 10,
    34  		Bucket: 4,
    35  	}
    36  
    37  	for _, f := range opt {
    38  		f(&o)
    39  	}
    40  
    41  	var mask uint16
    42  	if o.Bucket > 0 && o.Bucket&(o.Bucket-1) == 0 {
    43  		mask = o.Bucket - 1
    44  	} else {
    45  		o.Bucket |= o.Bucket >> 1
    46  		o.Bucket |= o.Bucket >> 2
    47  		o.Bucket |= o.Bucket >> 4
    48  		mask = o.Bucket | (o.Bucket >> 8)
    49  	}
    50  
    51  	c := &FastCache{
    52  		locks:    make([]sync.Mutex, mask+1),
    53  		insts:    make([][2]*lruCache, mask+1),
    54  		callback: o.Callback,
    55  		mask:     int32(mask),
    56  	}
    57  
    58  	for i := range c.insts {
    59  		c.insts[i][0] = &lruCache{dlList: make([][2]uint16, uint32(o.Cap)+1), nodes: make([]node, o.Cap), hashmap: make(map[string]uint16, o.Cap), last: 0}
    60  		if o.LRU2Cap > 0 {
    61  			c.insts[i][1] = &lruCache{dlList: make([][2]uint16, uint32(o.LRU2Cap)+1), nodes: make([]node, o.LRU2Cap), hashmap: make(map[string]uint16, o.LRU2Cap), last: 0}
    62  		}
    63  	}
    64  
    65  	if o.Expiration > 0 {
    66  		c.expiration = o.Expiration
    67  	}
    68  	return c
    69  }
    70  
    71  func (l *FastCache) set(k string, v *interface{}, b []byte, expiration ...time.Duration) {
    72  	if l.callback != nil {
    73  		if v != nil {
    74  			l.callback(SET, k, uintptr(unsafe.Pointer(v)))
    75  		} else {
    76  			l.callback(SET, k, uintptr(unsafe.Pointer(&b)))
    77  		}
    78  	}
    79  	idx := hasher(k) & l.mask
    80  	var expireAt int64
    81  	if len(expiration) > 0 {
    82  		if expiration[0] == -1 {
    83  		} else if expiration[0] > 0 {
    84  			expireAt = ztime.Clock()*1000 + int64(expiration[0])
    85  		} else if l.expiration > 0 {
    86  			expireAt = ztime.Clock()*1000 + int64(l.expiration)
    87  		}
    88  	} else if l.expiration > 0 {
    89  		expireAt = ztime.Clock()*1000 + int64(l.expiration)
    90  	}
    91  	l.locks[idx].Lock()
    92  	l.insts[idx][0].put(k, v, b, expireAt)
    93  	l.locks[idx].Unlock()
    94  }
    95  
    96  // Set an item into cache
    97  func (l *FastCache) Set(key string, val interface{}, expiration ...time.Duration) {
    98  	l.set(key, &val, nil, expiration...)
    99  }
   100  
   101  // SetBytes an item into cache
   102  func (l *FastCache) SetBytes(key string, b []byte) {
   103  	l.set(key, nil, b)
   104  }
   105  
   106  // Get value of key from cache with result
   107  func (l *FastCache) Get(key string) (interface{}, bool) {
   108  	if i, b, ok := l.get(key); ok {
   109  		if i != nil {
   110  			return *i, true
   111  		}
   112  		return b, true
   113  	}
   114  	return nil, false
   115  }
   116  
   117  // GetBytes value of key from cache with result
   118  func (l *FastCache) GetBytes(key string) ([]byte, bool) {
   119  	if i, b, ok := l.get(key); ok {
   120  		if b != nil {
   121  			return b, true
   122  		}
   123  		b, ok = (*i).([]byte)
   124  		return b, ok
   125  	}
   126  	return nil, false
   127  }
   128  
   129  // ProvideGet get value of key from cache with result and provide default value
   130  func (l *FastCache) ProvideGet(key string, provide func() (interface{}, bool), expiration ...time.Duration) (interface{}, bool) {
   131  	if i, _, ok := l.get(key); ok && i != nil {
   132  		return *i, true
   133  	}
   134  
   135  	_, _, _ = l.gsf.Do(key, func() (value interface{}, err error) {
   136  		value, ok := provide()
   137  		if ok {
   138  			l.Set(key, value, expiration...)
   139  		}
   140  		return
   141  	})
   142  
   143  	return l.Get(key)
   144  }
   145  
   146  func (l *FastCache) getValue(key string, idx, level int32) (*node, int) {
   147  	n, s := l.insts[idx][level].get(key)
   148  	if s > 0 && !n.isDelete && (n.expireAt == 0 || (ztime.Clock()*1000 <= n.expireAt)) {
   149  		return n, s
   150  	}
   151  	return nil, 0
   152  }
   153  
   154  func (l *FastCache) get(key string) (i *interface{}, b []byte, loaded bool) {
   155  	idx := hasher(key) & l.mask
   156  	l.locks[idx].Lock()
   157  	n, s := (*node)(nil), 0
   158  	if l.insts[idx][1] == nil {
   159  		n, s = l.getValue(key, idx, 0)
   160  	} else {
   161  		e := int64(0)
   162  		if n, s, e = l.insts[idx][0].delete(key); s <= 0 {
   163  			n, s = l.getValue(key, idx, 1)
   164  		} else {
   165  			l.insts[idx][1].put(key, n.value.value, n.value.byteValue, e)
   166  		}
   167  	}
   168  	if s <= 0 {
   169  		l.locks[idx].Unlock()
   170  		if l.callback != nil {
   171  			l.callback(GET, key, uintptr(0))
   172  		}
   173  		return
   174  	}
   175  	i, b = n.value.value, n.value.byteValue
   176  	l.locks[idx].Unlock()
   177  	if l.callback != nil {
   178  		if i != nil {
   179  			l.callback(GET, key, uintptr(unsafe.Pointer(i)))
   180  		} else {
   181  			var b interface{} = b
   182  			l.callback(GET, key, uintptr(unsafe.Pointer(&b)))
   183  		}
   184  	}
   185  	return i, b, true
   186  }
   187  
   188  // Delete item by key from cache
   189  func (l *FastCache) Delete(key string) {
   190  	idx := hasher(key) & l.mask
   191  	l.locks[idx].Lock()
   192  	n, s, e := l.insts[idx][0].delete(key)
   193  	if l.insts[idx][1] != nil {
   194  		if n2, s2, e2 := l.insts[idx][1].delete(key); n2 != nil && (n == nil || e < e2) {
   195  			n, s = n2, s2
   196  		}
   197  	}
   198  	if s > 0 {
   199  		if l.callback != nil {
   200  			if n.value.value != nil {
   201  				l.callback(DELETE, key, uintptr(unsafe.Pointer(n.value.value)))
   202  			} else {
   203  				l.callback(DELETE, key, uintptr(unsafe.Pointer(&n.value.byteValue)))
   204  			}
   205  		}
   206  		n.value.value, n.value.byteValue = nil, nil
   207  	} else if l.callback != nil {
   208  		l.callback(DELETE, key, uintptr(0))
   209  	}
   210  	l.locks[idx].Unlock()
   211  }
   212  
   213  // ForEach walk through all items in cache
   214  func (l *FastCache) ForEach(walker func(key string, iface interface{}) bool) {
   215  	for i := range l.insts {
   216  		l.locks[i].Lock()
   217  		if l.insts[i][0].forEach(walker); l.insts[i][1] != nil {
   218  			l.insts[i][1].forEach(walker)
   219  		}
   220  		l.locks[i].Unlock()
   221  	}
   222  }