package net.minecraft.util; import com.google.common.hash.HashCode; import com.google.common.hash.HashFunction; import com.google.common.hash.Hasher; import com.google.common.hash.Hashing; import com.mojang.datafixers.util.Pair; import com.mojang.serialization.DataResult; import com.mojang.serialization.DynamicOps; import com.mojang.serialization.ListBuilder; import com.mojang.serialization.MapLike; import com.mojang.serialization.RecordBuilder; import com.mojang.serialization.RecordBuilder.AbstractUniversalBuilder; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.IntStream; import java.util.stream.LongStream; import java.util.stream.Stream; public class HashOps implements DynamicOps { private static final byte TAG_EMPTY = 1; private static final byte TAG_MAP_START = 2; private static final byte TAG_MAP_END = 3; private static final byte TAG_LIST_START = 4; private static final byte TAG_LIST_END = 5; private static final byte TAG_BYTE = 6; private static final byte TAG_SHORT = 7; private static final byte TAG_INT = 8; private static final byte TAG_LONG = 9; private static final byte TAG_FLOAT = 10; private static final byte TAG_DOUBLE = 11; private static final byte TAG_STRING = 12; private static final byte TAG_BOOLEAN = 13; private static final byte TAG_BYTE_ARRAY_START = 14; private static final byte TAG_BYTE_ARRAY_END = 15; private static final byte TAG_INT_ARRAY_START = 16; private static final byte TAG_INT_ARRAY_END = 17; private static final byte TAG_LONG_ARRAY_START = 18; private static final byte TAG_LONG_ARRAY_END = 19; private static final byte[] EMPTY_PAYLOAD = new byte[]{1}; private static final byte[] FALSE_PAYLOAD = new byte[]{13, 0}; private static final byte[] TRUE_PAYLOAD = new byte[]{13, 1}; public static final byte[] EMPTY_MAP_PAYLOAD = new byte[]{2, 3}; public static final byte[] EMPTY_LIST_PAYLOAD = new byte[]{4, 5}; private static final DataResult UNSUPPORTED_OPERATION_ERROR = DataResult.error(() -> "Unsupported operation"); private static final Comparator HASH_COMPARATOR = Comparator.comparingLong(HashCode::padToLong); private static final Comparator> MAP_ENTRY_ORDER = Entry.comparingByKey(HASH_COMPARATOR) .thenComparing(Entry.comparingByValue(HASH_COMPARATOR)); private static final Comparator> MAPLIKE_ENTRY_ORDER = Comparator., HashCode>comparing(Pair::getFirst, HASH_COMPARATOR) .thenComparing(Pair::getSecond, HASH_COMPARATOR); public static final HashOps CRC32C_INSTANCE = new HashOps(Hashing.crc32c()); final HashFunction hashFunction; final HashCode empty; private final HashCode emptyMap; private final HashCode emptyList; private final HashCode trueHash; private final HashCode falseHash; public HashOps(HashFunction p_397329_) { this.hashFunction = p_397329_; this.empty = p_397329_.hashBytes(EMPTY_PAYLOAD); this.emptyMap = p_397329_.hashBytes(EMPTY_MAP_PAYLOAD); this.emptyList = p_397329_.hashBytes(EMPTY_LIST_PAYLOAD); this.falseHash = p_397329_.hashBytes(FALSE_PAYLOAD); this.trueHash = p_397329_.hashBytes(TRUE_PAYLOAD); } public HashCode empty() { return this.empty; } public HashCode emptyMap() { return this.emptyMap; } public HashCode emptyList() { return this.emptyList; } public HashCode createNumeric(Number p_397688_) { return switch (p_397688_) { case Byte obyte -> this.createByte(obyte); case Short oshort -> this.createShort(oshort); case Integer integer -> this.createInt(integer); case Long olong -> this.createLong(olong); case Double d0 -> this.createDouble(d0); case Float f -> this.createFloat(f); default -> this.createDouble(p_397688_.doubleValue()); }; } public HashCode createByte(byte p_396691_) { return this.hashFunction.newHasher(2).putByte((byte)6).putByte(p_396691_).hash(); } public HashCode createShort(short p_392813_) { return this.hashFunction.newHasher(3).putByte((byte)7).putShort(p_392813_).hash(); } public HashCode createInt(int p_395249_) { return this.hashFunction.newHasher(5).putByte((byte)8).putInt(p_395249_).hash(); } public HashCode createLong(long p_392135_) { return this.hashFunction.newHasher(9).putByte((byte)9).putLong(p_392135_).hash(); } public HashCode createFloat(float p_394026_) { return this.hashFunction.newHasher(5).putByte((byte)10).putFloat(p_394026_).hash(); } public HashCode createDouble(double p_394179_) { return this.hashFunction.newHasher(9).putByte((byte)11).putDouble(p_394179_).hash(); } public HashCode createString(String p_392797_) { return this.hashFunction.newHasher().putByte((byte)12).putInt(p_392797_.length()).putUnencodedChars(p_392797_).hash(); } public HashCode createBoolean(boolean p_397569_) { return p_397569_ ? this.trueHash : this.falseHash; } private static Hasher hashMap(Hasher p_397613_, Map p_396942_) { p_397613_.putByte((byte)2); p_396942_.entrySet() .stream() .sorted(MAP_ENTRY_ORDER) .forEach(p_392920_ -> p_397613_.putBytes(p_392920_.getKey().asBytes()).putBytes(p_392920_.getValue().asBytes())); p_397613_.putByte((byte)3); return p_397613_; } static Hasher hashMap(Hasher p_392551_, Stream> p_398023_) { p_392551_.putByte((byte)2); p_398023_.sorted(MAPLIKE_ENTRY_ORDER).forEach(p_392939_ -> p_392551_.putBytes(p_392939_.getFirst().asBytes()).putBytes(p_392939_.getSecond().asBytes())); p_392551_.putByte((byte)3); return p_392551_; } public HashCode createMap(Stream> p_396656_) { return hashMap(this.hashFunction.newHasher(), p_396656_).hash(); } public HashCode createMap(Map p_393057_) { return hashMap(this.hashFunction.newHasher(), p_393057_).hash(); } public HashCode createList(Stream p_398039_) { Hasher hasher = this.hashFunction.newHasher(); hasher.putByte((byte)4); p_398039_.forEach(p_397145_ -> hasher.putBytes(p_397145_.asBytes())); hasher.putByte((byte)5); return hasher.hash(); } public HashCode createByteList(ByteBuffer p_393066_) { Hasher hasher = this.hashFunction.newHasher(); hasher.putByte((byte)14); hasher.putBytes(p_393066_); hasher.putByte((byte)15); return hasher.hash(); } public HashCode createIntList(IntStream p_392378_) { Hasher hasher = this.hashFunction.newHasher(); hasher.putByte((byte)16); p_392378_.forEach(hasher::putInt); hasher.putByte((byte)17); return hasher.hash(); } public HashCode createLongList(LongStream p_395866_) { Hasher hasher = this.hashFunction.newHasher(); hasher.putByte((byte)18); p_395866_.forEach(hasher::putLong); hasher.putByte((byte)19); return hasher.hash(); } public HashCode remove(HashCode p_394547_, String p_395616_) { return p_394547_; } @Override public RecordBuilder mapBuilder() { return new HashOps.MapHashBuilder(); } @Override public ListBuilder listBuilder() { return new HashOps.ListHashBuilder(); } @Override public String toString() { return "Hash " + this.hashFunction; } public U convertTo(DynamicOps p_394392_, HashCode p_396939_) { throw new UnsupportedOperationException("Can't convert from this type"); } public Number getNumberValue(HashCode p_396836_, Number p_396683_) { return p_396683_; } public HashCode set(HashCode p_392609_, String p_394861_, HashCode p_396034_) { return p_392609_; } public HashCode update(HashCode p_393943_, String p_397765_, Function p_393638_) { return p_393943_; } public HashCode updateGeneric(HashCode p_394295_, HashCode p_397939_, Function p_391335_) { return p_394295_; } private static DataResult unsupported() { return (DataResult)UNSUPPORTED_OPERATION_ERROR; } public DataResult get(HashCode p_397791_, String p_393212_) { return unsupported(); } public DataResult getGeneric(HashCode p_392165_, HashCode p_395432_) { return unsupported(); } public DataResult getNumberValue(HashCode p_394009_) { return unsupported(); } public DataResult getBooleanValue(HashCode p_397324_) { return unsupported(); } public DataResult getStringValue(HashCode p_397603_) { return unsupported(); } public DataResult mergeToList(HashCode p_395328_, HashCode p_397519_) { return unsupported(); } public DataResult mergeToList(HashCode p_392335_, List p_393705_) { return unsupported(); } public DataResult mergeToMap(HashCode p_397103_, HashCode p_393644_, HashCode p_397786_) { return unsupported(); } public DataResult mergeToMap(HashCode p_394828_, Map p_392230_) { return unsupported(); } public DataResult mergeToMap(HashCode p_393002_, MapLike p_397657_) { return unsupported(); } public DataResult>> getMapValues(HashCode p_394873_) { return unsupported(); } public DataResult>> getMapEntries(HashCode p_395753_) { return unsupported(); } public DataResult> getStream(HashCode p_397363_) { return unsupported(); } public DataResult>> getList(HashCode p_391333_) { return unsupported(); } public DataResult> getMap(HashCode p_391961_) { return unsupported(); } public DataResult getByteBuffer(HashCode p_393051_) { return unsupported(); } public DataResult getIntStream(HashCode p_395829_) { return unsupported(); } public DataResult getLongStream(HashCode p_392986_) { return unsupported(); } class ListHashBuilder extends AbstractListBuilder { public ListHashBuilder() { super(HashOps.this); } protected Hasher initBuilder() { return HashOps.this.hashFunction.newHasher().putByte((byte)4); } protected Hasher append(Hasher p_394930_, HashCode p_392558_) { return p_394930_.putBytes(p_392558_.asBytes()); } protected DataResult build(Hasher p_394248_, HashCode p_391292_) { assert p_391292_.equals(HashOps.this.empty); p_394248_.putByte((byte)5); return DataResult.success(p_394248_.hash()); } } final class MapHashBuilder extends AbstractUniversalBuilder>> { public MapHashBuilder() { super(HashOps.this); } protected List> initBuilder() { return new ArrayList<>(); } protected List> append(HashCode p_391659_, HashCode p_392968_, List> p_394869_) { p_394869_.add(Pair.of(p_391659_, p_392968_)); return p_394869_; } protected DataResult build(List> p_396177_, HashCode p_392314_) { assert p_392314_.equals(HashOps.this.empty()); return DataResult.success(HashOps.hashMap(HashOps.this.hashFunction.newHasher(), p_396177_.stream()).hash()); } } }