diff --git a/util/util.go b/util/util.go index d4e0f27d..57e98c5e 100644 --- a/util/util.go +++ b/util/util.go @@ -391,6 +391,11 @@ func (cache *LRUCache) Get(key interface{}) (value interface{}, ok bool) { } func (cache *LRUCache) Put(key interface{}, value interface{}) { + if cache.capacity <= 0 { + // A cache with non-positive capacity holds nothing, so there is nothing + // to store and no eviction to perform. + return + } n, ok := cache.m[key] if ok { cache.remove(n, false) diff --git a/util/util_test.go b/util/util_test.go index 45082b52..b25f9b83 100644 --- a/util/util_test.go +++ b/util/util_test.go @@ -232,6 +232,18 @@ func TestLRUCache(t *testing.T) { testCacheEqual(t, cache, []int{1, 3, 4}) } +func TestLRUCacheNonPositiveCapacity(t *testing.T) { + // A cache created with a non-positive capacity holds nothing and must not + // panic on Put. Previously the eviction path removed cache.tail.prev, which + // on an empty list is the head sentinel whose prev pointer is nil, causing a + // nil pointer dereference. + cache := NewLRUCache(0) + cache.Put("a", 1) + if _, ok := cache.Get("a"); ok { + t.Errorf("NewLRUCache(0): Get(\"a\") ok = true, want false (zero-capacity cache holds nothing)") + } +} + func testEscapeStringLiterals(t *testing.T, input string, expected string) { t.Helper() result := EscapeStringLiterals(input)