From 2577a5a1681fab6c69f85ab03bb0489080851039 Mon Sep 17 00:00:00 2001 From: weisj <31143295+weisJ@users.noreply.github.com> Date: Wed, 11 Aug 2021 18:31:58 +0200 Subject: [PATCH] Util: Add cache which keeps a soft reference on the values The values need to implement the SoftCache.Cacheable interface where K is the key type. By doing this they promise to keep a reference to the key. Not doing so will eventually cause the value from being removed from the cache. --- utils/build.gradle.kts | 7 ++ .../weisj/darklaf/util/cache/SoftCache.java | 67 ++++++++++++++++ utils/src/main/module/module-info.java | 1 + .../darklaf/util/cache/SoftCacheTest.java | 78 +++++++++++++++++++ 4 files changed, 153 insertions(+) create mode 100644 utils/src/main/java/com/github/weisj/darklaf/util/cache/SoftCache.java create mode 100644 utils/src/test/java/com/github/weisj/darklaf/util/cache/SoftCacheTest.java diff --git a/utils/build.gradle.kts b/utils/build.gradle.kts index b4f3e8b7..40185bb7 100644 --- a/utils/build.gradle.kts +++ b/utils/build.gradle.kts @@ -5,4 +5,11 @@ plugins { dependencies { compileOnly(libs.nullabilityAnnotations) + + testImplementation(libs.test.junit.api) + testRuntimeOnly(libs.test.junit.engine) +} + +tasks.test { + useJUnitPlatform() } diff --git a/utils/src/main/java/com/github/weisj/darklaf/util/cache/SoftCache.java b/utils/src/main/java/com/github/weisj/darklaf/util/cache/SoftCache.java new file mode 100644 index 00000000..94ea3084 --- /dev/null +++ b/utils/src/main/java/com/github/weisj/darklaf/util/cache/SoftCache.java @@ -0,0 +1,67 @@ +/* + * MIT License + * + * Copyright (c) 2021 Jannis Weis + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package com.github.weisj.darklaf.util.cache; + +import java.lang.ref.Reference; +import java.lang.ref.SoftReference; +import java.util.Map; +import java.util.WeakHashMap; + +public class SoftCache> { + + private final Map> cache = new WeakHashMap<>(); + + public V get(final K key) { + Reference reference = cache.get(key); + if (reference == null) return null; + V value = reference.get(); + if (value == null) { + cache.remove(key); + } + return value; + } + + public void put(final K key, final V value) { + if (value != null) { + cache.put(key, new SoftReference<>(value)); + value.setCacheKey(key); + } else { + cache.remove(key); + } + } + + public void clear() { + cache.clear(); + } + + public int size() { + return cache.size(); + } + + public boolean isEmpty() { + return cache.isEmpty(); + } + + public interface Cacheable { + void setCacheKey(final K key); + } +} diff --git a/utils/src/main/module/module-info.java b/utils/src/main/module/module-info.java index 470e5bf7..f8703261 100644 --- a/utils/src/main/module/module-info.java +++ b/utils/src/main/module/module-info.java @@ -29,6 +29,7 @@ module darklaf.utils { requires static annotations; exports com.github.weisj.darklaf.util; + exports com.github.weisj.darklaf.util.cache; exports com.github.weisj.darklaf.util.graphics; exports com.github.weisj.darklaf.util.value; } diff --git a/utils/src/test/java/com/github/weisj/darklaf/util/cache/SoftCacheTest.java b/utils/src/test/java/com/github/weisj/darklaf/util/cache/SoftCacheTest.java new file mode 100644 index 00000000..b8b481f3 --- /dev/null +++ b/utils/src/test/java/com/github/weisj/darklaf/util/cache/SoftCacheTest.java @@ -0,0 +1,78 @@ +/* + * MIT License + * + * Copyright (c) 2021 Jannis Weis + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package com.github.weisj.darklaf.util.cache; + +import java.util.HashSet; +import java.util.Set; +import java.util.function.Supplier; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class SoftCacheTest { + + @Test + void testValuesAreReleased() { + final int count = 500; + SoftCache cache = new SoftCache<>(); + Set hardReferences = new HashSet<>(); + for (int i = 0; i < count; i++) { + TestValue value = new TestValue(); + TestKey key = new TestKey(); + hardReferences.add(value); + cache.put(key, value); + Assertions.assertEquals(key, value.key); + } + Assertions.assertEquals(count, hardReferences.size()); + Assertions.assertEquals(count, cache.size()); + + hardReferences.clear(); + + waitForGarbageCollection(() -> !cache.isEmpty()); + } + + @SuppressWarnings({"unused"}) + private void waitForGarbageCollection(final Supplier checker) { + while (checker.get()) { + Object object = null; + try { + object = new int[10][10][10][10][10][10][10][10][10][10][10][10]; + } catch (OutOfMemoryError ignored) { + } + System.out.println(object); + System.gc(); + } + } + + private static class TestKey { + } + + private static class TestValue implements SoftCache.Cacheable { + @SuppressWarnings({"FieldCanBeLocal", "unused"}) + private TestKey key; + + @Override + public void setCacheKey(TestKey key) { + this.key = key; + } + } +}