From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: 2No2Name <50278648+2No2Name@users.noreply.github.com>
Date: Mon, 15 Feb 2021 14:58:55 -0500
Subject: [PATCH] lithium: optimize `BlockPos.iterateOutwards` by caching
 offsets

Code taken from: https://github.com/CaffeineMC/lithium-fabric/pull/123 by 2No2Name licenced under the LGPLv3 License

diff --git a/src/main/java/me/jellysquid/mods/lithium/common/cached_blockpos_iteration/IterateOutwardsCache.java b/src/main/java/me/jellysquid/mods/lithium/common/cached_blockpos_iteration/IterateOutwardsCache.java
new file mode 100644
index 0000000000000000000000000000000000000000..50e9df6056f713b2f1eaf0cb4a23875e0c6c2e2c
--- /dev/null
+++ b/src/main/java/me/jellysquid/mods/lithium/common/cached_blockpos_iteration/IterateOutwardsCache.java
@@ -0,0 +1,72 @@
+package me.jellysquid.mods.lithium.common.cached_blockpos_iteration;
+
+import it.unimi.dsi.fastutil.longs.LongArrayList;
+import it.unimi.dsi.fastutil.longs.LongList;
+import net.minecraft.core.BlockPosition;
+
+import java.util.Iterator;
+import java.util.Random;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @author 2No2Name, original implemenation by SuperCoder7979 and Gegy1000
+ */
+public class IterateOutwardsCache {
+    //POS_ZERO must not be replaced with BlockPos.ORIGIN, otherwise iterateOutwards at BlockPos.ORIGIN will not use the cache
+    public static final BlockPosition POS_ZERO = new BlockPosition(0,0,0);
+
+
+    private final ConcurrentHashMap<Long, LongArrayList> table;
+    private final int capacity;
+    private final Random random;
+
+    public IterateOutwardsCache(int capacity) {
+        this.capacity = capacity;
+        this.table = new ConcurrentHashMap<>(31);
+        this.random = new Random();
+    }
+
+    private void fillPositionsWithIterateOutwards(LongList entry, int xRange, int yRange, int zRange) {
+        // Add all positions to the cached list
+        for (BlockPosition pos : BlockPosition.iterateOutwards(POS_ZERO, xRange, yRange, zRange)) {
+            entry.add(pos.asLong());
+        }
+    }
+
+    public LongList getOrCompute(int xRange, int yRange, int zRange) {
+        long key = BlockPosition.asLong(xRange, yRange, zRange);
+
+        LongArrayList entry = this.table.get(key);
+        if (entry != null) {
+            return entry;
+        }
+
+        // Cache miss: compute and store
+        entry = new LongArrayList(128);
+
+        this.fillPositionsWithIterateOutwards(entry, xRange, yRange, zRange);
+
+        //decrease the array size, as of now it won't be modified anymore anyways
+        entry.trim();
+
+        //this might overwrite an entry as the same entry could have been computed and added during this thread's computation
+        //we do not use computeIfAbsent, as it can delay other threads for too long
+        Object previousEntry = this.table.put(key, entry);
+
+
+        if (previousEntry == null && this.table.size() > this.capacity) {
+            //prevent a memory leak by randomly removing about 1/8th of the elements when the exceed the desired capacity is exceeded
+            final Iterator<Long> iterator = this.table.keySet().iterator();
+            //prevent an unlikely infinite loop caused by another thread filling the table concurrently using counting
+            for (int i = -this.capacity; iterator.hasNext() && i < 5; i++) {
+                Long key2 = iterator.next();
+                //random is not threadsafe, but it doesn't matter here, because we don't need quality random numbers
+                if (this.random.nextInt(8) == 0 && key2 != key) {
+                    iterator.remove();
+                }
+            }
+        }
+
+        return entry;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/me/jellysquid/mods/lithium/common/cached_blockpos_iteration/LongList2BlockPosMutableIterable.java b/src/main/java/me/jellysquid/mods/lithium/common/cached_blockpos_iteration/LongList2BlockPosMutableIterable.java
new file mode 100644
index 0000000000000000000000000000000000000000..e2e4f7968a399b4641df07b2931fff6dbc85ddb3
--- /dev/null
+++ b/src/main/java/me/jellysquid/mods/lithium/common/cached_blockpos_iteration/LongList2BlockPosMutableIterable.java
@@ -0,0 +1,47 @@
+package me.jellysquid.mods.lithium.common.cached_blockpos_iteration;
+
+import it.unimi.dsi.fastutil.longs.LongIterator;
+import it.unimi.dsi.fastutil.longs.LongList;
+import net.minecraft.core.BlockPosition;
+
+import java.util.Iterator;
+
+/**
+ * @author 2No2Name
+ */
+public class LongList2BlockPosMutableIterable implements Iterable<BlockPosition> {
+
+    private final LongList positions;
+    private final int xOffset, yOffset, zOffset;
+
+    public LongList2BlockPosMutableIterable(BlockPosition offset, LongList posList) {
+        this.xOffset = offset.getX();
+        this.yOffset = offset.getY();
+        this.zOffset = offset.getZ();
+        this.positions = posList;
+    }
+
+    @Override
+    public Iterator<BlockPosition> iterator() {
+        return new Iterator<BlockPosition>() {
+
+            private final LongIterator it = LongList2BlockPosMutableIterable.this.positions.iterator();
+            private final BlockPosition.MutableBlockPosition pos = new BlockPosition.MutableBlockPosition();
+
+            @Override
+            public boolean hasNext() {
+                return it.hasNext();
+            }
+
+            @Override
+            public BlockPosition next() {
+                long nextPos = this.it.nextLong();
+                return this.pos.setValues(
+                        LongList2BlockPosMutableIterable.this.xOffset + BlockPosition.unpackLongX(nextPos),
+                        LongList2BlockPosMutableIterable.this.yOffset + BlockPosition.unpackLongY(nextPos),
+                        LongList2BlockPosMutableIterable.this.zOffset + BlockPosition.unpackLongZ(nextPos));
+            }
+        };
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/net/minecraft/core/BlockPosition.java b/src/main/java/net/minecraft/core/BlockPosition.java
index 9c508cc41575175d442cd51d75706830351a24f2..df6df2ae3e30ada07c90a26b9615d939508d3246 100644
--- a/src/main/java/net/minecraft/core/BlockPosition.java
+++ b/src/main/java/net/minecraft/core/BlockPosition.java
@@ -19,10 +19,16 @@ import net.minecraft.world.phys.Vec3D;
 import org.apache.commons.lang3.Validate;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
+import me.jellysquid.mods.lithium.common.cached_blockpos_iteration.IterateOutwardsCache; // Yatopia
+import me.jellysquid.mods.lithium.common.cached_blockpos_iteration.LongList2BlockPosMutableIterable; // Yatopia
+import it.unimi.dsi.fastutil.longs.LongList; // Yatopia
 
 @Immutable
 public class BlockPosition extends BaseBlockPosition {
 
+    private static final IterateOutwardsCache ITERATE_OUTWARDS_CACHE = new IterateOutwardsCache(50); // Yatopia
+    private static final LongList HOGLIN_PIGLIN_CACHE = ITERATE_OUTWARDS_CACHE.getOrCompute(8, 4, 8); // Yatopia
+
     public static final Codec<BlockPosition> a = Codec.INT_STREAM.comapFlatMap((intstream) -> {
         return SystemUtils.a(intstream, 3).map((aint) -> {
             return new BlockPosition(aint[0], aint[1], aint[2]);
@@ -78,14 +84,17 @@ public class BlockPosition extends BaseBlockPosition {
         return a((int) (i >> 38) + j, (int) ((i << 52) >> 52) + k, (int) ((i << 26) >> 38) + l); // Paper - simplify/inline
     }
 
+    public static int unpackLongX(long i) { return BlockPosition.b(i); } // Yatopia - OBFHELPER
     public static int b(long i) {
         return (int) (i >> 38); // Paper - simplify/inline
     }
 
+    public static int unpackLongY(long i) { return BlockPosition.c(i); } // Yatopia - OBFHELPER
     public static int c(long i) {
         return (int) ((i << 52) >> 52); // Paper - simplify/inline
     }
 
+    public static int unpackLongZ(long i) { return BlockPosition.d(i); } // Yatopia - OBFHELPER
     public static int d(long i) {
         return (int) ((i << 26) >> 38);  // Paper - simplify/inline
     }
@@ -266,7 +275,15 @@ public class BlockPosition extends BaseBlockPosition {
         };
     }
 
+    public static Iterable<BlockPosition> iterateOutwards(BlockPosition blockposition, int p_i, int p_j, int p_k) { return BlockPosition.a(blockposition, p_i, p_j, p_k); } // Yatopia - OBFHELPER
     public static Iterable<BlockPosition> a(BlockPosition blockposition, int p_i, int p_j, int p_k) { // Paper - decompile issues - variable name conflicts to inner class field refs
+        // Yatopia start - lithium: optimize `BlockPos.iterateOutwards` by caching offsets
+        if (blockposition != me.jellysquid.mods.lithium.common.cached_blockpos_iteration.IterateOutwardsCache.POS_ZERO) {
+            final LongList positions = p_i == 8 && p_j == 4 && p_k == 8 ? HOGLIN_PIGLIN_CACHE : ITERATE_OUTWARDS_CACHE.getOrCompute(p_i, p_j, p_j);
+            return new LongList2BlockPosMutableIterable(blockposition, positions);
+        }
+        // Yatopia end
+
         int l_decompiled = p_i + p_j + p_k; // Paper - decompile issues
         int i1 = blockposition.getX();
         int j1 = blockposition.getY();
