github.com/insolar/vanilla@v0.0.0-20201023172447-248fdf805322/cachekit/cache_example.go (about)

     1  // Copyright 2020 Insolar Network Ltd.
     2  // All rights reserved.
     3  // This material is licensed under the Insolar License version 1.0,
     4  // available at https://github.com/insolar/assured-ledger/blob/master/LICENSE.md.
     5  
     6  package cachekit
     7  
     8  import (
     9  	"github.com/insolar/vanilla/throw"
    10  )
    11  
    12  type Key = uint64
    13  type Value = string
    14  
    15  const zeroValue = ""
    16  
    17  /*
    18  	UintCache is an example/template implementation of a cache that uses the Core.
    19  
    20  	To create a new cache type - copy this file, rename struct, set Key and Value type aliases accordingly, then
    21  	set zeroValue constant in accordance with Value type.
    22  
    23  
    24   */
    25  
    26  func NewUintCache(cs Strategy) *UintCache {
    27  	c := &UintCache{
    28  		keys: map[Key]Index{},
    29  		values: [][]uintEntry { make([]uintEntry, cs.AllocationPageSize()) },
    30  	}
    31  	c.core = NewCore(cs, c.trimBatch)
    32  
    33  	return c
    34  }
    35  
    36  // UintCache is an example/template implementation of a cache that uses the Core.
    37  type UintCache struct {
    38  	values [][]uintEntry
    39  	keys   map[Key]Index
    40  
    41  	core   Core
    42  }
    43  
    44  type uintEntry struct {
    45  	key Key
    46  	value Value
    47  }
    48  
    49  // Allocated returns a total number of cache entries allocated, but some of them may be unused.
    50  // NB! Cache can only grow.
    51  func (p *UintCache) Allocated() int {
    52  	return len(p.values) * cap(p.values[0])
    53  }
    54  
    55  // Occupied returns a number of added / available cache entries.
    56  func (p *UintCache) Occupied() int {
    57  	return len(p.keys)
    58  }
    59  
    60  // Put adds value with the given key.
    61  // If key was already added, then cached value remains unchanged and the function returns (false).
    62  // Access to the key is always updated.
    63  func (p *UintCache) Put(key Key, value Value) bool {
    64  	idx, ok := p.keys[key]
    65  	if ok {
    66  		p.core.Touch(idx)
    67  		return false
    68  	}
    69  	idx, _ = p.core.Add()
    70  	p.keys[key] = idx
    71  	p.putEntry(idx, uintEntry{key, value})
    72  	return true
    73  }
    74  
    75  // Replace adds or replaces value with the given key. If key was already added, then cached value is updated and the function returns (false).
    76  // Access to the key is always updated.
    77  func (p *UintCache) Replace(key Key, value Value) bool {
    78  	idx, ok := p.keys[key]
    79  	if ok {
    80  		p.core.Touch(idx)
    81  		p.putEntry(idx, uintEntry{key, value})
    82  		return false
    83  	}
    84  	idx, _ = p.core.Add()
    85  	p.keys[key] = idx
    86  	p.putEntry(idx, uintEntry{key, value})
    87  	return true
    88  }
    89  
    90  // Get returns value and presence flag for the given key.
    91  // Access to the key is updated when key exists.
    92  func (p *UintCache) Get(key Key) (Value, bool) {
    93  	idx, ok := p.keys[key]
    94  	if !ok {
    95  		return zeroValue, false
    96  	}
    97  	p.core.Touch(idx)
    98  	ce := p.getEntry(idx)
    99  	return ce.value, true
   100  }
   101  
   102  // Peek returns value and presence flag for the given key.
   103  // Access to the key is not updated.
   104  func (p *UintCache) Peek(key Key) (Value, bool) {
   105  	idx, ok := p.keys[key]
   106  	if !ok {
   107  		return zeroValue, false
   108  	}
   109  	ce := p.getEntry(idx)
   110  	return ce.value, true
   111  }
   112  
   113  // Contains returns (true) when the key is present.
   114  // Access to the key is not updated.
   115  func (p *UintCache) Contains(key Key) bool {
   116  	_, ok := p.keys[key]
   117  	return ok
   118  }
   119  
   120  // Delete removes key and zero out relevant value. Returns (false) when key wasn't present.
   121  // Access to the key is not updated. Cache entry will become unavailable, but will only be freed after relevant expiry / eviction.
   122  func (p *UintCache) Delete(key Key) bool {
   123  	idx, ok := p.keys[key]
   124  	if !ok {
   125  		return false
   126  	}
   127  	ce := p.getEntry(idx)
   128  	if ce.key != key {
   129  		panic(throw.IllegalState())
   130  	}
   131  	p.core.Delete(idx)
   132  	delete(p.keys, ce.key)
   133  	*ce = uintEntry{}
   134  	return true
   135  }
   136  
   137  func (p *UintCache) putEntry(idx Index, entry uintEntry) {
   138  	pgSize := cap(p.values[0])
   139  	pgN := idx / pgSize
   140  
   141  	if pgN == len(p.values) {
   142  		p.values = append(p.values, make([]uintEntry, pgSize))
   143  	}
   144  	p.values[pgN][idx % pgSize] = entry
   145  }
   146  
   147  func (p *UintCache) getEntry(idx Index) *uintEntry {
   148  	pgSize := cap(p.values[0])
   149  	return &p.values[idx / pgSize][idx % pgSize]
   150  }
   151  
   152  func (p *UintCache) trimBatch(trimmed []uint32) {
   153  	for _, idx := range trimmed {
   154  		ce := p.getEntry(Index(idx))
   155  		delete(p.keys, ce.key)
   156  		*ce = uintEntry{}
   157  	}
   158  }
   159