Skip to content

Commit

Permalink
refactor: Replace interface{} by any
Browse files Browse the repository at this point in the history
  • Loading branch information
TwiN committed Nov 2, 2022
1 parent 268181c commit eab552f
Show file tree
Hide file tree
Showing 6 changed files with 43 additions and 40 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ func main() {

cache.Set("key", "value")
cache.SetWithTTL("key-with-ttl", "value", 60*time.Minute)
cache.SetAll(map[string]interface{}{"k1": "v1", "k2": "v2", "k3": "v3"})
cache.SetAll(map[string]any{"k1": "v1", "k2": "v2", "k3": "v3"})

fmt.Println("[Count] Cache size:", cache.Count())

Expand Down
8 changes: 4 additions & 4 deletions entry.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type Entry struct {
Key string

// Value is the value of the cache entry
Value interface{}
Value any

// RelevantTimestamp is the variable used to store either:
// - creation timestamp, if the Cache's EvictionPolicy is FirstInFirstOut
Expand Down Expand Up @@ -48,7 +48,7 @@ func (entry *Entry) SizeInBytes() int {
return toBytes(entry.Key) + toBytes(entry.Value) + 32
}

func toBytes(value interface{}) int {
func toBytes(value any) int {
switch value.(type) {
case string:
return int(unsafe.Sizeof(value)) + len(value.(string))
Expand All @@ -60,9 +60,9 @@ func toBytes(value interface{}) int {
return int(unsafe.Sizeof(value)) + 4
case int64, uint64, int, uint, float64, complex128:
return int(unsafe.Sizeof(value)) + 8
case []interface{}:
case []any:
size := 0
for _, v := range value.([]interface{}) {
for _, v := range value.([]any) {
size += toBytes(v)
}
return int(unsafe.Sizeof(value)) + size
Expand Down
4 changes: 2 additions & 2 deletions entry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@ func TestEntry_SizeInBytes(t *testing.T) {
testSizeInBytes(t, "k", struct{ A string }{A: "hello"}, 72)
testSizeInBytes(t, "k", struct{ A, B string }{A: "hello", B: "world"}, 78)
testSizeInBytes(t, "k", nil, 70)
testSizeInBytes(t, "k", make([]interface{}, 5), 170)
testSizeInBytes(t, "k", make([]any, 5), 170)
}

func testSizeInBytes(t *testing.T, key string, value interface{}, expectedSize int) {
func testSizeInBytes(t *testing.T, key string, value any, expectedSize int) {
t.Run(fmt.Sprintf("%T_%d", value, expectedSize), func(t *testing.T) {
if size := (&Entry{Key: key, Value: value}).SizeInBytes(); size != expectedSize {
t.Errorf("expected size of entry with key '%v' and value '%v' (%T) to be %d, got %d", key, value, value, expectedSize, size)
Expand Down
57 changes: 30 additions & 27 deletions gocache.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,25 +165,27 @@ func (cache *Cache) WithEvictionPolicy(policy EvictionPolicy) *Cache {
// is nil or not.
//
// If set to true (default):
// cache := gocache.NewCache().WithForceNilInterfaceOnNilPointer(true)
// cache.Set("key", (*Struct)(nil))
// value, _ := cache.Get("key")
// // the following returns true, because the interface{} was forcefully set to nil
// if value == nil {}
// // the following will panic, because the value has been casted to its type (which is nil)
// if value.(*Struct) == nil {}
//
// cache := gocache.NewCache().WithForceNilInterfaceOnNilPointer(true)
// cache.Set("key", (*Struct)(nil))
// value, _ := cache.Get("key")
// // the following returns true, because the interface{} (any) was forcefully set to nil
// if value == nil {}
// // the following will panic, because the value has been casted to its type (which is nil)
// if value.(*Struct) == nil {}
//
// If set to false:
// cache := gocache.NewCache().WithForceNilInterfaceOnNilPointer(false)
// cache.Set("key", (*Struct)(nil))
// value, _ := cache.Get("key")
// // the following returns false, because the interface{} returned has a non-nil type (*Struct)
// if value == nil {}
// // the following returns true, because the value has been casted to its type
// if value.(*Struct) == nil {}
//
// cache := gocache.NewCache().WithForceNilInterfaceOnNilPointer(false)
// cache.Set("key", (*Struct)(nil))
// value, _ := cache.Get("key")
// // the following returns false, because the interface{} (any) returned has a non-nil type (*Struct)
// if value == nil {}
// // the following returns true, because the value has been cast to its type
// if value.(*Struct) == nil {}
//
// In other words, if set to true, you do not need to cast the value returned from the cache to
// to check if the value is nil.
// check if the value is nil.
//
// Defaults to true
func (cache *Cache) WithForceNilInterfaceOnNilPointer(forceNilInterfaceOnNilPointer bool) *Cache {
Expand All @@ -194,8 +196,8 @@ func (cache *Cache) WithForceNilInterfaceOnNilPointer(forceNilInterfaceOnNilPoin
// NewCache creates a new Cache
//
// Should be used in conjunction with Cache.WithMaxSize, Cache.WithMaxMemoryUsage and/or Cache.WithEvictionPolicy
// gocache.NewCache().WithMaxSize(10000).WithEvictionPolicy(gocache.LeastRecentlyUsed)
//
// gocache.NewCache().WithMaxSize(10000).WithEvictionPolicy(gocache.LeastRecentlyUsed)
func NewCache() *Cache {
return &Cache{
maxSize: DefaultMaxSize,
Expand All @@ -209,15 +211,15 @@ func NewCache() *Cache {
}

// Set creates or updates a key with a given value
func (cache *Cache) Set(key string, value interface{}) {
func (cache *Cache) Set(key string, value any) {
cache.SetWithTTL(key, value, NoExpiration)
}

// SetWithTTL creates or updates a key with a given value and sets an expiration time (-1 is NoExpiration)
//
// The TTL provided must be greater than 0, or NoExpiration (-1). If a negative value that isn't -1 (NoExpiration) is
// provided, the entry will not be created if the key doesn't exist
func (cache *Cache) SetWithTTL(key string, value interface{}, ttl time.Duration) {
func (cache *Cache) SetWithTTL(key string, value any, ttl time.Duration) {
// An interface is only nil if both its value and its type are nil, however, passing a nil pointer as an interface{}
// means that the interface itself is not nil, because the interface value is nil but not the type.
if cache.forceNilInterfaceOnNilPointer {
Expand Down Expand Up @@ -298,7 +300,7 @@ func (cache *Cache) SetWithTTL(key string, value interface{}, ttl time.Duration)
}

// SetAll creates or updates multiple values
func (cache *Cache) SetAll(entries map[string]interface{}) {
func (cache *Cache) SetAll(entries map[string]any) {
for key, value := range entries {
cache.SetWithTTL(key, value, NoExpiration)
}
Expand All @@ -307,7 +309,7 @@ func (cache *Cache) SetAll(entries map[string]interface{}) {
// Get retrieves an entry using the key passed as parameter
// If there is no such entry, the value returned will be nil and the boolean will be false
// If there is an entry, the value returned will be the value cached and the boolean will be true
func (cache *Cache) Get(key string) (interface{}, bool) {
func (cache *Cache) Get(key string) (any, bool) {
cache.mutex.Lock()
entry, ok := cache.get(key)
if !ok {
Expand Down Expand Up @@ -337,7 +339,7 @@ func (cache *Cache) Get(key string) (interface{}, bool) {

// GetValue retrieves an entry using the key passed as parameter
// Unlike Get, this function only returns the value
func (cache *Cache) GetValue(key string) interface{} {
func (cache *Cache) GetValue(key string) any {
value, _ := cache.Get(key)
return value
}
Expand All @@ -346,8 +348,8 @@ func (cache *Cache) GetValue(key string) interface{} {
// All keys are returned in the map, regardless of whether they exist or not, however, entries that do not exist in the
// cache will return nil, meaning that there is no way of determining whether a key genuinely has the value nil, or
// whether it doesn't exist in the cache using only this function.
func (cache *Cache) GetByKeys(keys []string) map[string]interface{} {
entries := make(map[string]interface{})
func (cache *Cache) GetByKeys(keys []string) map[string]any {
entries := make(map[string]any)
for _, key := range keys {
entries[key], _ = cache.Get(key)
}
Expand All @@ -365,8 +367,8 @@ func (cache *Cache) GetByKeys(keys []string) map[string]interface{} {
// GetKeysByPattern is a good alternative if you want to retrieve entries that you do not have the key for, as it only
// retrieves the keys and does not trigger active eviction and has a parameter for setting a limit to the number of keys
// you wish to retrieve.
func (cache *Cache) GetAll() map[string]interface{} {
entries := make(map[string]interface{})
func (cache *Cache) GetAll() map[string]any {
entries := make(map[string]any)
cache.mutex.Lock()
for key, entry := range cache.entries {
if entry.Expired() {
Expand All @@ -385,8 +387,9 @@ func (cache *Cache) GetAll() map[string]interface{} {
// If the limit is above 0, the search will stop once the specified number of matching keys have been found.
//
// e.g.
// cache.GetKeysByPattern("*some*", 0) will return all keys containing "some" in them
// cache.GetKeysByPattern("*some*", 5) will return 5 keys (or less) containing "some" in them
//
// cache.GetKeysByPattern("*some*", 0) will return all keys containing "some" in them
// cache.GetKeysByPattern("*some*", 5) will return 5 keys (or less) containing "some" in them
//
// Note that GetKeysByPattern does not trigger active evictions, nor does it count as accessing the entry (if LRU).
// The reason for that behavior is that these two (active eviction and access) only applies when you access the value
Expand Down
4 changes: 2 additions & 2 deletions gocache_bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
)

func BenchmarkMap_Get(b *testing.B) {
m := make(map[string]interface{})
m := make(map[string]any)
for n := 0; n < b.N; n++ {
_, _ = m[strconv.Itoa(n)]
}
Expand All @@ -24,7 +24,7 @@ func BenchmarkMap_Set(b *testing.B) {
}
for name, value := range values {
b.Run(fmt.Sprintf("%s value", name), func(b *testing.B) {
m := make(map[string]interface{})
m := make(map[string]any)
for n := 0; n < b.N; n++ {
m[strconv.Itoa(n)] = value
}
Expand Down
8 changes: 4 additions & 4 deletions gocache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ func TestCache_SetGetStruct(t *testing.T) {

func TestCache_SetAll(t *testing.T) {
cache := NewCache().WithMaxSize(NoMaxSize)
cache.SetAll(map[string]interface{}{"k1": "v1", "k2": "v2"})
cache.SetAll(map[string]any{"k1": "v1", "k2": "v2"})
value, ok := cache.Get("k1")
if !ok {
t.Error("expected key to exist")
Expand All @@ -351,7 +351,7 @@ func TestCache_SetAll(t *testing.T) {
if value != "v2" {
t.Errorf("expected: %s, but got: %s", "v2", value)
}
cache.SetAll(map[string]interface{}{"k1": "updated"})
cache.SetAll(map[string]any{"k1": "updated"})
value, ok = cache.Get("k1")
if !ok {
t.Error("expected key to exist")
Expand Down Expand Up @@ -949,7 +949,7 @@ func TestCache_MemoryUsageIsReliable(t *testing.T) {
t.Error("cache.MemoryUsage() should've increased")
}
previousCacheMemoryUsage = cache.MemoryUsage()
cache.SetAll(map[string]interface{}{"2": "2", "3": "3", "4": "4"})
cache.SetAll(map[string]any{"2": "2", "3": "3", "4": "4"})
if cache.MemoryUsage() <= previousCacheMemoryUsage {
t.Error("cache.MemoryUsage() should've increased")
}
Expand Down Expand Up @@ -1003,7 +1003,7 @@ func TestCache_WithForceNilInterfaceOnNilPointer(t *testing.T) {
t.Error("expected key to exist")
} else {
if value != nil {
// the value is not nil, because cache.Get returns an interface{}, and the type of that interface is not nil
// the value is not nil, because cache.Get returns an interface{} (any), and the type of that interface is not nil
t.Error("value should be nil")
}
}
Expand Down

0 comments on commit eab552f

Please sign in to comment.