Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactored the cache to use generics #1

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@
# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Dependency directories (remove the comment below to include it)
# vendor/
# Ignoring compiled .go files.
*.go
25 changes: 6 additions & 19 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,23 +1,10 @@
language: go

go:
- "1.13.x"
- "1.14.x"
- tip
services:
- docker

before_install:
- go get golang.org/x/tools/cmd/cover
- go get -t -v ./...

# Anything in before_script that returns a nonzero exit code will flunk the
# build and immediately stop. It's sorta like having set -e enabled in bash.
# We can download and extract the golangci-lint binary in one (long) command.
before_script:
- curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $GOPATH/bin v1.24.0
# This builds the docker file containing the tests
- docker build . -t lfucache

script:
- golangci-lint run
- go test -v -race -coverprofile=coverage.txt -covermode=atomic

after_success:
- bash <(curl -s https://codecov.io/bash)
# This tests the go2 files
- docker run --rm lfucache go tool go2go test
10 changes: 10 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
FROM levonet/golang:go2go

RUN apt-get update && apt-get install git -y

RUN git clone https://github.com/dgrijalva/lfu-go /go/src/github.com/dgrijalva/lfu-go && \
git clone https://github.com/arschles/assert /go/src/github.com/arschles/assert

COPY ./ /go/src/myapp

WORKDIR /go/src/myapp
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Lest Frequently Used (LFU) Cache
==================================
[![Build Status](https://travis-ci.com/NdoleStudio/lfu-cache.svg?branch=master)](https://travis-ci.com/NdoleStudio/lfu-cache)
[![Build Status](https://travis-ci.com/NdoleStudio/lfu-cache.svg?branch=2.0-dev)](https://travis-ci.com/NdoleStudio/lfu-cache)
[![codecov](https://codecov.io/gh/NdoleStudio/lfu-cache/branch/master/graph/badge.svg)](https://codecov.io/gh/NdoleStudio/lfu-cache)
[![Go Report Card](https://goreportcard.com/badge/github.com/NdoleStudio/lfu-cache)](https://goreportcard.com/report/github.com/NdoleStudio/lfu-cache)
[![GitHub contributors](https://img.shields.io/github/contributors/NdoleStudio/lfu-cache)](https://github.com/NdoleStudio/lfu-cache/graphs/contributors)
Expand Down
52 changes: 25 additions & 27 deletions cache.go → cache.go2
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,28 @@
// All operations have with O(1) complexity.
// When evicting an item from the cache,
// if 2 items have the same frequency the (least recently used) LRU item is evicted.
//
// Ideally, you should provide a wrapper around this class
// to ensure strict type checks for keys and values that can be put into the cache.
//
package lfucache

import (
"container/list"

"github.com/pkg/errors"
"errors"
)

type lookupTableNodeMap(type Key, Value) map[interface{}]*lookupTableNode(Key, Value)


// Cache is the data structure for the LFU Cache.
// The zero value of this cache is not ready to use because the cap is zero
type Cache struct {
type Cache (type Key, Value) struct {
cap int
frequencyList *list.List
lookupTable map[interface{}]*lookupTableNode
lookupTable lookupTableNodeMap(Key, Value)
}

// lookupTableNode is a hash map for the items in the lfu Cache.
type lookupTableNode struct {
value interface{}
frequencyListNodeListNode *frequencyListNodeListNode
type lookupTableNode (type Key, Value) struct {
value Value
frequencyListNodeListNode *frequencyListNodeListNode(Key)
}

// frequentListNode is an element in the frequency list
Expand All @@ -37,9 +35,9 @@ type frequencyListNode struct {
}

// frequencyListNodeListNode is an item in the frequency list linked list for a particular weight.
type frequencyListNodeListNode struct {
type frequencyListNodeListNode(type Key) struct {
parent *frequencyListNode
key interface{}
key Key
element *list.Element
}

Expand All @@ -56,43 +54,43 @@ const minFrequencyWeight = 1

// New creates a new instance of the LFU Cache.
// It returns and ErrInvalidCap error if the cap <= 0.
func New(cap int) (cache *Cache, err error) {
func New(type Key, Value) (cap int) (cache *Cache(Key, Value), err error) {
if cap <= 0 {
return cache, ErrInvalidCap
}

cache = &Cache{
cache = &Cache(Key, Value){
cap: cap,
frequencyList: list.New(),
lookupTable: make(map[interface{}]*lookupTableNode, cap),
lookupTable: make(lookupTableNodeMap(Key, Value), cap),
}

return cache, err
}

// Len returns the number of items in the Cache.
func (cache *Cache) Len() int {
func (cache *Cache(Key, Value)) Len() int {
return len(cache.lookupTable)
}

// Cap returns the cap fo the Cache.
func (cache *Cache) Cap() int {
func (cache *Cache(Key, Value)) Cap() int {
return cache.cap
}

// IsFull determines if the Cache is full.
func (cache *Cache) IsFull() bool {
func (cache *Cache(Key, Value)) IsFull() bool {
return cache.Len() == cache.Cap()
}

// IsEmpty determines if there are no items in the Cache.
func (cache *Cache) IsEmpty() bool {
func (cache *Cache(Key, Value)) IsEmpty() bool {
return cache.Len() == 0
}

// Set is used to an item in the Cache with key and value.
// It returns ErrInvalidCap if the cache is not initialized.
func (cache *Cache) Set(key interface{}, value interface{}) (err error) {
func (cache *Cache(Key, Value)) Set(key Key, value Value) (err error) {
// check if cache has been initialized.
if cache.Cap() <= 0 {
return ErrInvalidCap
Expand All @@ -105,7 +103,7 @@ func (cache *Cache) Set(key interface{}, value interface{}) (err error) {
}

// create a lookup table node.
lookupTableNode := &lookupTableNode{value: value}
lookupTableNode := &lookupTableNode(Key, Value){value: value}

// check if the lookupTable has enough space. If it doesn't, pop the least frequently used item from the Cache.
if cache.IsFull() {
Expand All @@ -125,7 +123,7 @@ func (cache *Cache) Set(key interface{}, value interface{}) (err error) {
freqListNode := cache.frequencyList.Front().Value.(*frequencyListNode)

// set the node parent of the newly set item in the frequency list node Cache.
freqListNodeListNode := &frequencyListNodeListNode{parent: freqListNode, key: key}
freqListNodeListNode := &frequencyListNodeListNode(Key){parent: freqListNode, key: key}

// set the frequencyListNodeListNode in the lookup table node.
lookupTableNode.frequencyListNodeListNode = freqListNodeListNode
Expand All @@ -137,7 +135,7 @@ func (cache *Cache) Set(key interface{}, value interface{}) (err error) {
}

// Get returns an item for the Cache having a key. It returns ErrCacheMiss if there's a Cache miss.
func (cache *Cache) Get(key interface{}) (value interface{}, err error) {
func (cache *Cache(Key, Value)) Get(key Key) (value Value, err error) {
// check if the key exists if it doesn't return with a Cache miss error.
node, ok := cache.lookupTable[key]
if !ok {
Expand Down Expand Up @@ -176,9 +174,9 @@ func (cache *Cache) Get(key interface{}) (value interface{}, err error) {
}

// pop removes the least frequently used item from the Cache.
func (cache *Cache) pop() {
func (cache *Cache(Key, Value)) pop() {
// The frequency list node MUST exist i.e the cache cap.
freqListNodeListNode := cache.frequencyList.Front().Value.(*frequencyListNode).list.Front().Value.(*frequencyListNodeListNode)
freqListNodeListNode := cache.frequencyList.Front().Value.(*frequencyListNode).list.Front().Value.(*frequencyListNodeListNode(Key))

// Remove key from lookup table.
delete(cache.lookupTable, freqListNodeListNode.key)
Expand All @@ -190,4 +188,4 @@ func (cache *Cache) pop() {
if cache.frequencyList.Front().Value.(*frequencyListNode).list.Len() == 0 {
cache.frequencyList.Remove(cache.frequencyList.Front())
}
}
}
Loading