Pool FullDataPointIdMap Entries and improve hash logic
This commit is contained in:
+92
-8
@@ -27,12 +27,12 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrappe
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
/**
|
||||
@@ -131,7 +131,7 @@ public class FullDataPointIdMap
|
||||
* If an entry with the given values already exists nothing will
|
||||
* be added but the existing item's ID will still be returned.
|
||||
*/
|
||||
public int addIfNotPresentAndGetId(IBiomeWrapper biome, IBlockStateWrapper blockState) { return this.addIfNotPresentAndGetId(new Entry(biome, blockState), true); }
|
||||
public int addIfNotPresentAndGetId(IBiomeWrapper biome, IBlockStateWrapper blockState) { return this.addIfNotPresentAndGetId(Entry.getEntry(biome, blockState), true); }
|
||||
/** @param useWriteLocks should only be false if this method is already in a write lock to prevent unlocking at the wrong time */
|
||||
private int addIfNotPresentAndGetId(Entry biomeBlockStateEntry, boolean useWriteLocks)
|
||||
{
|
||||
@@ -322,15 +322,81 @@ public class FullDataPointIdMap
|
||||
{
|
||||
private static final IWrapperFactory WRAPPER_FACTORY = SingletonInjector.INSTANCE.get(IWrapperFactory.class);
|
||||
|
||||
private static final Int2ReferenceOpenHashMap<ArrayList<Entry>> ENTRY_POOL = new Int2ReferenceOpenHashMap<>();
|
||||
/** lock is necessary since {@link Int2ReferenceOpenHashMap} isn't concurrent and concurrent threads can cause infinite loops */
|
||||
private static final ReentrantReadWriteLock ENTRY_POOL_LOCK = new ReentrantReadWriteLock();
|
||||
|
||||
public final IBiomeWrapper biome;
|
||||
public final IBlockStateWrapper blockState;
|
||||
|
||||
private Integer hashCode = null;
|
||||
|
||||
|
||||
// constructor //
|
||||
|
||||
public Entry(IBiomeWrapper biome, IBlockStateWrapper blockState)
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public static Entry getEntry(IBiomeWrapper biome, IBlockStateWrapper blockState)
|
||||
{
|
||||
int entryHash = getHashCode(biome, blockState);
|
||||
|
||||
// try getting the existing entry
|
||||
try
|
||||
{
|
||||
ENTRY_POOL_LOCK.readLock().lock();
|
||||
|
||||
// check if an entry already exists
|
||||
ArrayList<Entry> entryList = ENTRY_POOL.get(entryHash);
|
||||
if (entryList != null)
|
||||
{
|
||||
// at least one entry exists with this hash code
|
||||
for (int i = 0; i < entryList.size(); i++)
|
||||
{
|
||||
Entry entry = entryList.get(i);
|
||||
if (entry.biome.equals(biome) && entry.blockState.equals(blockState))
|
||||
{
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
// if we got here, then there was a hash collision and this entry wasn't present in the array
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
ENTRY_POOL_LOCK.readLock().unlock();
|
||||
}
|
||||
|
||||
|
||||
// no entry exists,
|
||||
// create a new one
|
||||
try
|
||||
{
|
||||
ENTRY_POOL_LOCK.writeLock().lock();
|
||||
|
||||
ArrayList<Entry> entryList = ENTRY_POOL.get(entryHash);
|
||||
if (entryList == null)
|
||||
{
|
||||
// no entries exist for this hash code
|
||||
|
||||
// we assume that hash collisions should basically never happen,
|
||||
// so the array starts with an initial capacity of 1.
|
||||
// However, since collisions will eventually happen, using an arrayList prevents unexpected bugs caused by collisions.
|
||||
entryList = new ArrayList<>(1);
|
||||
ENTRY_POOL.put(entryHash, entryList);
|
||||
}
|
||||
|
||||
Entry newEntry = new Entry(biome, blockState);
|
||||
entryList.add(newEntry);
|
||||
return newEntry;
|
||||
}
|
||||
finally
|
||||
{
|
||||
ENTRY_POOL_LOCK.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
private Entry(IBiomeWrapper biome, IBlockStateWrapper blockState)
|
||||
{
|
||||
this.biome = biome;
|
||||
this.blockState = blockState;
|
||||
@@ -338,15 +404,29 @@ public class FullDataPointIdMap
|
||||
|
||||
|
||||
|
||||
// methods //
|
||||
//===========//
|
||||
// overrides //
|
||||
//===========//
|
||||
|
||||
public static int getHashCode(Entry entry) { return getHashCode(entry.biome, entry.blockState); }
|
||||
public static int getHashCode(IBiomeWrapper biome, IBlockStateWrapper blockState)
|
||||
{
|
||||
final int prime = 31;
|
||||
|
||||
int result = 1;
|
||||
// the biome and blockstate hashcode should be already calculated by the time
|
||||
// we get here, so this operation should be very fast
|
||||
result = prime * result + (biome == null ? 0 : biome.hashCode());
|
||||
result = prime * result + (blockState == null ? 0 : blockState.hashCode());
|
||||
return result;
|
||||
}
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
// cache the hash code to improve speed
|
||||
if (this.hashCode == null)
|
||||
{
|
||||
this.hashCode = this.serialize().hashCode();
|
||||
this.hashCode = getHashCode(this);
|
||||
}
|
||||
|
||||
return this.hashCode;
|
||||
@@ -371,6 +451,10 @@ public class FullDataPointIdMap
|
||||
|
||||
|
||||
|
||||
//=================//
|
||||
// (de)serializing //
|
||||
//=================//
|
||||
|
||||
public String serialize() { return this.biome.getSerialString() + BLOCK_STATE_SEPARATOR_STRING + this.blockState.getSerialString(); }
|
||||
|
||||
public static Entry deserialize(String str, ILevelWrapper levelWrapper) throws IOException, InterruptedException
|
||||
@@ -389,7 +473,7 @@ public class FullDataPointIdMap
|
||||
|
||||
IBiomeWrapper biome = WRAPPER_FACTORY.deserializeBiomeWrapper(stringArray[0], levelWrapper);
|
||||
IBlockStateWrapper blockState = WRAPPER_FACTORY.deserializeBlockStateWrapper(stringArray[1], levelWrapper);
|
||||
return new Entry(biome, blockState);
|
||||
return Entry.getEntry(biome, blockState);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -40,6 +40,11 @@ public class FullDataSourceV2Repo extends AbstractDhRepo<DhSectionPos, FullDataS
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public FullDataSourceV2Repo(String databaseType, String databaseLocation) throws SQLException
|
||||
{
|
||||
super(databaseType, databaseLocation, FullDataSourceV2DTO.class);
|
||||
@@ -47,6 +52,10 @@ public class FullDataSourceV2Repo extends AbstractDhRepo<DhSectionPos, FullDataS
|
||||
|
||||
|
||||
|
||||
//===========//
|
||||
// overrides //
|
||||
//===========//
|
||||
|
||||
@Override
|
||||
public String getTableName() { return "FullData"; }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user