From ae21012ea22c0fd1bc319097fe7371115e0d0710 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Sun, 29 May 2022 15:25:39 -0400 Subject: [PATCH] Use a proper key implementation for the search cache (#254) * Use a proper key implementation for the search cache * Synchronize itemSearchNames accesses to prevent CMEs * Use a ConcurrentHashMap instead of synchronizing This allows multiple threads to read at the same time, which is the more common scenario. --- .../java/codechicken/nei/api/ItemInfo.java | 53 ++++++++++++++++--- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/src/main/java/codechicken/nei/api/ItemInfo.java b/src/main/java/codechicken/nei/api/ItemInfo.java index b23e01c95..61743b252 100644 --- a/src/main/java/codechicken/nei/api/ItemInfo.java +++ b/src/main/java/codechicken/nei/api/ItemInfo.java @@ -58,6 +58,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; /** * This is an internal class for storing information about items, to be accessed by the API @@ -82,8 +83,36 @@ public static enum Layout public static final HashMap itemOwners = new HashMap<>(); + private static class ItemStackKey { + public final ItemStack stack; + public ItemStackKey(ItemStack stack) { + this.stack = stack; + } + + @Override + public int hashCode() { + if(this.stack == null) + return 1; + int hashCode = 1; + hashCode = 31 * hashCode + stack.stackSize; + hashCode = 31 * hashCode + Item.getIdFromItem(stack.getItem()); + hashCode = 31 * hashCode + stack.getItemDamage(); + hashCode = 31 * hashCode + (!stack.hasTagCompound() ? 0 : stack.getTagCompound().hashCode()); + return hashCode; + } + + @Override + public boolean equals(Object o) { + if(o == this) + return true; + if(!(o instanceof ItemStackKey)) + return false; + return ItemStack.areItemStacksEqual(this.stack, ((ItemStackKey)o).stack); + } + } + //lookup optimisation - public static final HashMap itemSearchNames = new HashMap<>(); + public static final ConcurrentHashMap itemSearchNames = new ConcurrentHashMap<>(); public static boolean isHidden(ItemStack stack) { return hiddenItems.contains(stack); @@ -129,7 +158,18 @@ public static void load(World world) { } private static void addSearchOptimisation() { - ItemList.loadCallbacks.add(itemSearchNames::clear); + ItemList.loadCallbacks.add(ItemInfo::populateSearchMap); + } + + private static void populateSearchMap() { + /* Create a snapshot of the current keys in the cache */ + HashSet oldItems = new HashSet<>(itemSearchNames.keySet()); + for(ItemStack stack : ItemList.items) { + /* Populate each entry and remove it from the snapshot */ + getSearchName(stack); + oldItems.remove(new ItemStackKey(stack)); + } + itemSearchNames.keySet().removeAll(oldItems); } private static void addHiddenItemFilter() { @@ -517,11 +557,8 @@ public static List getText(ItemStack itemStack, World world, EntityPlaye } public static String getSearchName(ItemStack stack) { - String s = itemSearchNames.get(stack); - if(s == null) { - s = EnumChatFormatting.getTextWithoutFormattingCodes(GuiContainerManager.concatenatedDisplayName(stack, true).toLowerCase()); - itemSearchNames.put(stack, s); - } - return s; + return itemSearchNames.computeIfAbsent(new ItemStackKey(stack), key -> + EnumChatFormatting.getTextWithoutFormattingCodes(GuiContainerManager.concatenatedDisplayName(key.stack, true).toLowerCase()) + ); } }