From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Thu, 11 Mar 2021 20:05:44 -0800
Subject: [PATCH] Custom table implementation for blockstate state lookups

Testing some redstone intensive machines showed to bring about a 10%
improvement.

diff --git a/src/main/java/com/tuinity/tuinity/util/table/ZeroCollidingReferenceStateTable.java b/src/main/java/com/tuinity/tuinity/util/table/ZeroCollidingReferenceStateTable.java
new file mode 100644
index 0000000000000000000000000000000000000000..6985facdb2d1689a71472d3ac282e4f5f3cb9869
--- /dev/null
+++ b/src/main/java/com/tuinity/tuinity/util/table/ZeroCollidingReferenceStateTable.java
@@ -0,0 +1,160 @@
+package com.tuinity.tuinity.util.table;
+
+import com.google.common.collect.Table;
+import net.minecraft.world.level.block.state.IBlockDataHolder;
+import net.minecraft.world.level.block.state.properties.IBlockState;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+public final class ZeroCollidingReferenceStateTable {
+
+    // upper 32 bits: starting index
+    // lower 32 bits: bitset for contained ids
+    protected final long[] this_index_table;
+    protected final Comparable<?>[] this_table;
+    protected final IBlockDataHolder<?, ?> this_state;
+
+    protected long[] index_table;
+    protected IBlockDataHolder<?, ?>[][] value_table;
+
+    public ZeroCollidingReferenceStateTable(final IBlockDataHolder<?, ?> state, final Map<IBlockState<?>, Comparable<?>> this_map) {
+        this.this_state = state;
+        this.this_index_table = this.create_table(this_map.keySet());
+
+        int max_id = -1;
+        for (final IBlockState<?> property : this_map.keySet()) {
+            final int id = lookup_vindex(property, this.this_index_table);
+            if (id > max_id) {
+                max_id = id;
+            }
+        }
+
+        this.this_table = new Comparable[max_id + 1];
+        for (final Map.Entry<IBlockState<?>, Comparable<?>> entry : this_map.entrySet()) {
+            this.this_table[lookup_vindex(entry.getKey(), this.this_index_table)] = entry.getValue();
+        }
+    }
+
+    public void loadInTable(final Table<IBlockState<?>, Comparable<?>, IBlockDataHolder<?, ?>> table,
+                            final Map<IBlockState<?>, Comparable<?>> this_map) {
+        final Set<IBlockState<?>> combined = new HashSet<>(table.rowKeySet());
+        combined.addAll(this_map.keySet());
+
+        this.index_table = this.create_table(combined);
+
+        int max_id = -1;
+        for (final IBlockState<?> property : combined) {
+            final int id = lookup_vindex(property, this.index_table);
+            if (id > max_id) {
+                max_id = id;
+            }
+        }
+
+        this.value_table = new IBlockDataHolder[max_id + 1][];
+
+        final Map<IBlockState<?>, Map<Comparable<?>, IBlockDataHolder<?, ?>>> map = table.rowMap();
+        for (final IBlockState<?> property : map.keySet()) {
+            final Map<Comparable<?>, IBlockDataHolder<?, ?>> propertyMap = map.get(property);
+
+            final int id = lookup_vindex(property, this.index_table);
+            final IBlockDataHolder<?, ?>[] states = this.value_table[id] = new IBlockDataHolder[property.getValues().size()];
+
+            for (final Map.Entry<Comparable<?>, IBlockDataHolder<?, ?>> entry : propertyMap.entrySet()) {
+                if (entry.getValue() == null) {
+                    // TODO what
+                    continue;
+                }
+
+                states[((IBlockState)property).getIdFor(entry.getKey())] = entry.getValue();
+            }
+        }
+
+
+        for (final Map.Entry<IBlockState<?>, Comparable<?>> entry : this_map.entrySet()) {
+            final IBlockState<?> property = entry.getKey();
+            final int index = lookup_vindex(property, this.index_table);
+
+            if (this.value_table[index] == null) {
+                this.value_table[index] = new IBlockDataHolder[property.getValues().size()];
+            }
+
+            this.value_table[index][((IBlockState)property).getIdFor(entry.getValue())] = this.this_state;
+        }
+    }
+
+
+    protected long[] create_table(final Collection<IBlockState<?>> collection) {
+        int max_id = -1;
+        for (final IBlockState<?> property : collection) {
+            final int id = property.getId();
+            if (id > max_id) {
+                max_id = id;
+            }
+        }
+
+        final long[] ret = new long[((max_id + 1) + 31) >>> 5]; // ceil((max_id + 1) / 32)
+
+        for (final IBlockState<?> property : collection) {
+            final int id = property.getId();
+
+            ret[id >>> 5] |= (1L << (id & 31));
+        }
+
+        int total = 0;
+        for (int i = 1, len = ret.length; i < len; ++i) {
+            ret[i] |= (long)(total += Long.bitCount(ret[i - 1] & 0xFFFFFFFFL)) << 32;
+        }
+
+        return ret;
+    }
+
+    public Comparable<?> get(final IBlockState<?> state) {
+        final Comparable<?>[] table = this.this_table;
+        final int index = lookup_vindex(state, this.this_index_table);
+
+        if (index < 0 || index >= table.length) {
+            return null;
+        }
+        return table[index];
+    }
+
+    public IBlockDataHolder<?, ?> get(final IBlockState<?> property, final Comparable<?> with) {
+        final int withId = ((IBlockState)property).getIdFor(with);
+        if (withId < 0) {
+            return null;
+        }
+
+        final int index = lookup_vindex(property, this.index_table);
+        final IBlockDataHolder<?, ?>[][] table = this.value_table;
+        if (index < 0 || index >= table.length) {
+            return null;
+        }
+
+        final IBlockDataHolder<?, ?>[] values = table[index];
+
+        if (withId >= values.length) {
+            return null;
+        }
+
+        return values[withId];
+    }
+
+    protected static int lookup_vindex(final IBlockState<?> property, final long[] index_table) {
+        final int id = property.getId();
+        final long bitset_mask = (1L << (id & 31));
+        final long lower_mask = bitset_mask - 1;
+        final int index = id >>> 5;
+        if (index >= index_table.length) {
+            return -1;
+        }
+        final long index_value = index_table[index];
+        final long contains_check = ((index_value & bitset_mask) - 1) >> (Long.SIZE - 1); // -1L if doesn't contain
+
+        // index = total bits set in lower table values (upper 32 bits of index_value) plus total bits set in lower indices below id
+        // contains_check is 0 if the bitset had id set, else it's -1: so index is unaffected if contains_check == 0,
+        // otherwise it comes out as -1.
+        return (int)(((index_value >>> 32) + Long.bitCount(index_value & lower_mask)) | contains_check);
+    }
+}
diff --git a/src/main/java/net/minecraft/world/level/block/state/IBlockDataHolder.java b/src/main/java/net/minecraft/world/level/block/state/IBlockDataHolder.java
index ba046cffdd8331c7e0427f19fa54d0c7a99077d9..9f9a3210a68e489dcdeabaeeddf01b59775eebd2 100644
--- a/src/main/java/net/minecraft/world/level/block/state/IBlockDataHolder.java
+++ b/src/main/java/net/minecraft/world/level/block/state/IBlockDataHolder.java
@@ -40,11 +40,13 @@ public abstract class IBlockDataHolder<O, S> {
     private final ImmutableMap<IBlockState<?>, Comparable<?>> b;
     private Table<IBlockState<?>, Comparable<?>, S> e;
     protected final MapCodec<S> d;
+    protected com.tuinity.tuinity.util.table.ZeroCollidingReferenceStateTable optimisedTable; // Tuinity - optimise state lookup
 
     protected IBlockDataHolder(O o0, ImmutableMap<IBlockState<?>, Comparable<?>> immutablemap, MapCodec<S> mapcodec) {
         this.c = o0;
         this.b = immutablemap;
         this.d = mapcodec;
+        this.optimisedTable = new com.tuinity.tuinity.util.table.ZeroCollidingReferenceStateTable(this, immutablemap); // Tuinity - optimise state lookup
     }
 
     public <T extends Comparable<T>> S a(IBlockState<T> iblockstate) {
@@ -86,11 +88,11 @@ public abstract class IBlockDataHolder<O, S> {
 
     public <T extends Comparable<T>> boolean contains(IBlockState<T> iblockstate) { return this.b(iblockstate); } // Paper - OBFHELPER
     public <T extends Comparable<T>> boolean b(IBlockState<T> iblockstate) {
-        return this.b.containsKey(iblockstate);
+        return this.optimisedTable.get(iblockstate) != null; // Tuinity - optimise state lookup
     }
 
     public <T extends Comparable<T>> T get(IBlockState<T> iblockstate) {
-        Comparable<?> comparable = (Comparable) this.b.get(iblockstate);
+        final Comparable<?> comparable = this.optimisedTable.get(iblockstate); // Tuinity - optimise state lookup
 
         if (comparable == null) {
             throw new IllegalArgumentException("Cannot get property " + iblockstate + " as it does not exist in " + this.c);
@@ -100,27 +102,21 @@ public abstract class IBlockDataHolder<O, S> {
     }
 
     public <T extends Comparable<T>> Optional<T> d(IBlockState<T> iblockstate) {
-        Comparable<?> comparable = (Comparable) this.b.get(iblockstate);
+        final Comparable<?> comparable = this.optimisedTable.get(iblockstate); // Tuinity - optimise state lookup
 
         return comparable == null ? Optional.empty() : Optional.of(iblockstate.getType().cast(comparable));
     }
 
     public <T extends Comparable<T>, V extends T> S set(IBlockState<T> iblockstate, V v0) {
-        Comparable<?> comparable = (Comparable) this.b.get(iblockstate);
+        // Tuinity start - optimise state lookup
+        final S ret = (S)this.optimisedTable.get(iblockstate, v0);
 
-        if (comparable == null) {
-            throw new IllegalArgumentException("Cannot set property " + iblockstate + " as it does not exist in " + this.c);
-        } else if (comparable == v0) {
-            return (S) this; // Paper - decompile error
-        } else {
-            S s0 = this.e.get(iblockstate, v0);
-
-            if (s0 == null) {
-                throw new IllegalArgumentException("Cannot set property " + iblockstate + " to " + v0 + " on " + this.c + ", it is not an allowed value");
-            } else {
-                return s0;
-            }
+        if (ret == null) {
+            throw new IllegalArgumentException("Cannot set property " + iblockstate + " to " + v0 + " on " + this.c + ", it is not an allowed value");
         }
+
+        return ret;
+        // Tuinity end - optimise state lookup
     }
 
     public void a(Map<Map<IBlockState<?>, Comparable<?>>, S> map) {
@@ -144,7 +140,8 @@ public abstract class IBlockDataHolder<O, S> {
                 }
             }
 
-            this.e = (Table) (table.isEmpty() ? table : ArrayTable.create(table));
+            this.e = (Table) (table.isEmpty() ? table : ArrayTable.create(table)); this.optimisedTable.loadInTable((Table)this.e, this.b); // Tuinity - optimise state lookup
+
         }
     }
 
diff --git a/src/main/java/net/minecraft/world/level/block/state/properties/BlockStateBoolean.java b/src/main/java/net/minecraft/world/level/block/state/properties/BlockStateBoolean.java
index f2f94950681b198ae7a4c31a044fd62e98e448ab..2c7306c2b0c1d9e701bc3aa2c115434abeab3d72 100644
--- a/src/main/java/net/minecraft/world/level/block/state/properties/BlockStateBoolean.java
+++ b/src/main/java/net/minecraft/world/level/block/state/properties/BlockStateBoolean.java
@@ -12,6 +12,13 @@ public class BlockStateBoolean extends IBlockState<Boolean> {
         super(s, Boolean.class);
     }
 
+    // Tuinity start - optimise iblockdata state lookup
+    @Override
+    public final int getIdFor(final Boolean value) {
+        return value.booleanValue() ? 1 : 0;
+    }
+    // Tuinity end - optimise iblockdata state lookup
+
     @Override
     public Collection<Boolean> getValues() {
         return this.a;
diff --git a/src/main/java/net/minecraft/world/level/block/state/properties/BlockStateEnum.java b/src/main/java/net/minecraft/world/level/block/state/properties/BlockStateEnum.java
index 3079cd13ea1465f4221fde4fec7df639f7c1eb49..a11069695b64e515f0e76e1d6ccddc33503492cd 100644
--- a/src/main/java/net/minecraft/world/level/block/state/properties/BlockStateEnum.java
+++ b/src/main/java/net/minecraft/world/level/block/state/properties/BlockStateEnum.java
@@ -18,6 +18,15 @@ public class BlockStateEnum<T extends Enum<T> & INamable> extends IBlockState<T>
     private final ImmutableSet<T> a;
     private final Map<String, T> b = Maps.newHashMap();
 
+    // Tuinity start - optimise iblockdata state lookup
+    private int[] idLookupTable;
+
+    @Override
+    public final int getIdFor(final T value) {
+        return this.idLookupTable[value.ordinal()];
+    }
+    // Tuinity end - optimise iblockdata state lookup
+
     protected BlockStateEnum(String s, Class<T> oclass, Collection<T> collection) {
         super(s, oclass);
         this.a = ImmutableSet.copyOf(collection);
@@ -33,6 +42,14 @@ public class BlockStateEnum<T extends Enum<T> & INamable> extends IBlockState<T>
 
             this.b.put(s1, t0);
         }
+        // Tuinity start - optimise iblockdata state lookup
+        int id = 0;
+        this.idLookupTable = new int[oclass.getEnumConstants().length];
+        java.util.Arrays.fill(this.idLookupTable, -1);
+        for (final T value : this.getValues()) {
+            this.idLookupTable[value.ordinal()] = id++;
+        }
+        // Tuinity end - optimise iblockdata state lookup
 
     }
 
diff --git a/src/main/java/net/minecraft/world/level/block/state/properties/BlockStateInteger.java b/src/main/java/net/minecraft/world/level/block/state/properties/BlockStateInteger.java
index 190978c889222185b47065e9e5f96a82e59c7b4e..e8165d4de4acca241924aa4076fc8868e1f453d9 100644
--- a/src/main/java/net/minecraft/world/level/block/state/properties/BlockStateInteger.java
+++ b/src/main/java/net/minecraft/world/level/block/state/properties/BlockStateInteger.java
@@ -13,6 +13,16 @@ public class BlockStateInteger extends IBlockState<Integer> {
     public final int min;
     public final int max;
 
+    // Tuinity start - optimise iblockdata state lookup
+    @Override
+    public final int getIdFor(final Integer value) {
+        final int val = value.intValue();
+        final int ret = val - this.min;
+
+        return ret | ((this.max - ret) >> 31);
+    }
+    // Tuinity end - optimise iblockdata state lookup
+
     protected BlockStateInteger(String s, int i, int j) {
         super(s, Integer.class);
         this.min = i;
diff --git a/src/main/java/net/minecraft/world/level/block/state/properties/IBlockState.java b/src/main/java/net/minecraft/world/level/block/state/properties/IBlockState.java
index 759d6a4adaa511488ace5e2650eb685cbb6c4c16..21250d10a51bd0697709f0aa059908f9f71491d5 100644
--- a/src/main/java/net/minecraft/world/level/block/state/properties/IBlockState.java
+++ b/src/main/java/net/minecraft/world/level/block/state/properties/IBlockState.java
@@ -16,6 +16,17 @@ public abstract class IBlockState<T extends Comparable<T>> {
     private final Codec<T> d;
     private final Codec<IBlockState.a<T>> e;
 
+    // Tuinity start - optimise iblockdata state lookup
+    private static final java.util.concurrent.atomic.AtomicInteger ID_GENERATOR = new java.util.concurrent.atomic.AtomicInteger();
+    private final int id = ID_GENERATOR.getAndIncrement();
+
+    public final int getId() {
+        return this.id;
+    }
+
+    public abstract int getIdFor(final T value);
+    // Tuinity end - optimise state lookup
+
     protected IBlockState(String s, Class<T> oclass) {
         this.d = Codec.STRING.comapFlatMap((s1) -> this.b(s1).map(DataResult::success).orElseGet(() -> { // Paper - decompile error
             return DataResult.error("Unable to read property: " + this + " with value: " + s1);
