func (c *SizedLRU) Len() int {
	return len(c.cache)
}
Len returns the number of items in the cache
package disk
import (
	"container/list"
	"errors"
	"fmt"
	"github.com/prometheus/client_golang/prometheus"
)
type Key interface{}
type EvictCallback func(key Key, value lruItem)
type SizedLRU struct {
	ll *list.List
	cache map[interface{}]*list.Element
	currentSize int64
	uncompressedSize int64
	reservedSize int64
	maxSize int64
	onEvict EvictCallback
	gaugeCacheSizeBytes     prometheus.Gauge
	gaugeCacheLogicalBytes  prometheus.Gauge
	counterEvictedBytes     prometheus.Counter
	counterOverwrittenBytes prometheus.Counter
}
type entry struct {
	key   Key
	value lruItem
}
const BlockSize = 4096
func NewSizedLRU(maxSize int64, onEvict EvictCallback) SizedLRU {
	return SizedLRU{
		maxSize: maxSize,
		ll:      list.New(),
		cache:   make(map[interface{}]*list.Element),
		onEvict: onEvict,
		gaugeCacheSizeBytes: prometheus.NewGauge(prometheus.GaugeOpts{
			Name: "bazel_remote_disk_cache_size_bytes",
			Help: "The current number of bytes in the disk backend",
		}),
		gaugeCacheLogicalBytes: prometheus.NewGauge(prometheus.GaugeOpts{
			Name: "bazel_remote_disk_cache_logical_bytes",
			Help: "The current number of bytes in the disk backend if they were uncompressed",
		}),
		counterEvictedBytes: prometheus.NewCounter(prometheus.CounterOpts{
			Name: "bazel_remote_disk_cache_evicted_bytes_total",
			Help: "The total number of bytes evicted from disk backend, due to full cache",
		}),
		counterOverwrittenBytes: prometheus.NewCounter(prometheus.CounterOpts{
			Name: "bazel_remote_disk_cache_overwritten_bytes_total",
			Help: "The total number of bytes removed from disk backend, due to put of already existing key",
		}),
	}
}
func (c *SizedLRU) RegisterMetrics() {
	prometheus.MustRegister(c.gaugeCacheSizeBytes)
	prometheus.MustRegister(c.gaugeCacheLogicalBytes)
	prometheus.MustRegister(c.counterEvictedBytes)
	prometheus.MustRegister(c.counterOverwrittenBytes)
}
func (c *SizedLRU) Add(key Key, value lruItem) (ok bool) {
	roundedUpSizeOnDisk := roundUp4k(value.sizeOnDisk)
	if roundedUpSizeOnDisk > c.maxSize {
		return false
	}
	var sizeDelta, uncompressedSizeDelta int64
	if ee, ok := c.cache[key]; ok {
		sizeDelta = roundedUpSizeOnDisk - roundUp4k(ee.Value.(*entry).value.sizeOnDisk)
		if c.reservedSize+sizeDelta > c.maxSize {
			return false
		}
		uncompressedSizeDelta = roundUp4k(value.size) - roundUp4k(ee.Value.(*entry).value.size)
		c.ll.MoveToFront(ee)
		c.counterOverwrittenBytes.Add(float64(ee.Value.(*entry).value.sizeOnDisk))
		prevValue := ee.Value.(*entry).value
		if c.onEvict != nil {
			c.onEvict(key, prevValue)
		}
		ee.Value.(*entry).value = value
	} else {
		sizeDelta = roundedUpSizeOnDisk
		if c.reservedSize+sizeDelta > c.maxSize {
			return false
		}
		uncompressedSizeDelta = roundUp4k(value.size)
		ele := c.ll.PushFront(&entry{key, value})
		c.cache[key] = ele
	}
	for c.currentSize+sizeDelta > c.maxSize {
		ele := c.ll.Back()
		if ele != nil {
			c.removeElement(ele)
		}
	}
	c.currentSize += sizeDelta
	c.uncompressedSize += uncompressedSizeDelta
	c.gaugeCacheSizeBytes.Set(float64(c.currentSize))
	c.gaugeCacheLogicalBytes.Set(float64(c.uncompressedSize))
	return true
}
func (c *SizedLRU) Get(key Key) (value lruItem, ok bool) {
	if ele, hit := c.cache[key]; hit {
		c.ll.MoveToFront(ele)
		return ele.Value.(*entry).value, true
	}
	return
}
func (c *SizedLRU) Remove(key Key) {
	if ele, hit := c.cache[key]; hit {
		c.removeElement(ele)
		c.gaugeCacheSizeBytes.Set(float64(c.currentSize))
		c.gaugeCacheLogicalBytes.Set(float64(c.uncompressedSize))
	}
}