Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ Original license: GPL-3.0-only
Original project: https://github.com/embeddedt/ModernFix

diff --git a/net/minecraft/nbt/CompoundTag.java b/net/minecraft/nbt/CompoundTag.java
index c9c050966924ddd315e7829e7f3aee2afacc106b..6e859b1add79ce4e636e2994f59963da2cb440a9 100644
index c9c050966924ddd315e7829e7f3aee2afacc106b..35b9e48de8c1d49f1603f493b6a83ffd1e302036 100644
--- a/net/minecraft/nbt/CompoundTag.java
+++ b/net/minecraft/nbt/CompoundTag.java
@@ -54,7 +54,7 @@ public final class CompoundTag implements Tag {
@@ -54,13 +54,13 @@ public final class CompoundTag implements Tag {

private static CompoundTag loadCompound(DataInput input, NbtAccounter nbtAccounter) throws IOException {
nbtAccounter.accountBytes(48L);
Expand All @@ -19,6 +19,22 @@ index c9c050966924ddd315e7829e7f3aee2afacc106b..6e859b1add79ce4e636e2994f59963da

byte b;
while ((b = input.readByte()) != 0) {
String string = readString(input, nbtAccounter);
Tag namedTagData = CompoundTag.readNamedTagData(TagTypes.getType(b), string, input, nbtAccounter);
- if (map.put(string, namedTagData) == null) {
+ if (map.put(org.dreeam.leaf.util.map.StringCanonizingOpenHashMap.intern(string), namedTagData) == null) { // Leaf - Further reduce memory footprint of CompoundTag
nbtAccounter.accountBytes(36L);
}
}
@@ -101,7 +101,7 @@ public final class CompoundTag implements Tag {
type.skip(input, nbtAccounter);
break;
default:
- String string = readString(input, nbtAccounter);
+ String string = org.dreeam.leaf.util.map.StringCanonizingOpenHashMap.intern(readString(input, nbtAccounter)); // Leaf - Further reduce memory footprint of CompoundTag
switch (visitor.visitEntry(type, string)) {
case HALT:
return StreamTagVisitor.ValueResult.HALT;
@@ -171,7 +171,7 @@ public final class CompoundTag implements Tag {
}

Expand All @@ -28,13 +44,19 @@ index c9c050966924ddd315e7829e7f3aee2afacc106b..6e859b1add79ce4e636e2994f59963da
}

@Override
@@ -400,6 +400,11 @@ public final class CompoundTag implements Tag {
@@ -400,6 +400,17 @@ public final class CompoundTag implements Tag {

@Override
public CompoundTag copy() {
+ // Leaf start - Further reduce memory footprint of CompoundTag
+ if (this.tags instanceof org.dreeam.leaf.util.map.StringCanonizingOpenHashMap<Tag> stringCanonizingTags) {
+ return new CompoundTag(org.dreeam.leaf.util.map.StringCanonizingOpenHashMap.deepCopy(stringCanonizingTags, Tag::copy));
+ org.dreeam.leaf.util.map.StringCanonizingOpenHashMap<Tag> ret = new org.dreeam.leaf.util.map.StringCanonizingOpenHashMap<>(stringCanonizingTags.size(), 0.8f);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious about how much improvement this patch can bring, as we ignored the load factor of the original map when copying, rehashing cost is expensive.

I suggest add another method getLoadFactor in the StringCanonizingOpenHashMap to reuse load factor

+ it.unimi.dsi.fastutil.objects.ObjectIterator<it.unimi.dsi.fastutil.objects.Object2ObjectMap.Entry<String, Tag>> iterator = stringCanonizingTags.object2ObjectEntrySet().fastIterator();
+ while (iterator.hasNext()) {
+ Map.Entry<String, Tag> entry = iterator.next();
+ ret.put(entry.getKey(), entry.getValue().copy());
+ }
+ return new CompoundTag(ret);
+ }
+ // Leaf end - Further reduce memory footprint of CompoundTag
// Paper start - Reduce memory footprint of CompoundTag
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,14 @@
import com.google.common.collect.Interner;
import com.google.common.collect.Interners;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectIterator;

import java.util.Map;
import java.util.function.Function;

public class StringCanonizingOpenHashMap<T> extends Object2ObjectOpenHashMap<String, T> {

private static final Interner<String> KEY_INTERNER = Interners.newBuilder().weak().concurrencyLevel(16).<String>build();
private static final Interner<String> KEY_INTERNER = Interners.newBuilder().weak().concurrencyLevel(16).build();

private static String intern(String key) {
public static String intern(String key) {
return key != null ? KEY_INTERNER.intern(key) : null;
}

Expand All @@ -29,32 +27,24 @@ public StringCanonizingOpenHashMap(int expectedSize, float loadFactor) {
}

@Override
public T put(String key, T value) {
return super.put(intern(key), value);
}

@Override
public void putAll(Map<? extends String, ? extends T> m) {
if (m.isEmpty()) return;
ensureCapacity(size() + m.size());
for (Map.Entry<? extends String, ? extends T> entry : m.entrySet()) {
super.put(intern(entry.getKey()), entry.getValue());
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Map<?, ?> m)) return false;
if (m.size() != size()) return false;
if (containsNullKey) {
if (!value[n].equals(m.get(key[n]))) {
Copy link
Copy Markdown
Member

@HaHaWTH HaHaWTH Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we use Objects.equals here to guard against null values? And use m.containsKey(key) to ensure the key exists, null == null is always true

return false;
}
}
}

private void putWithoutInterning(String key, T value) {
super.put(key, value);
}

public static <T> StringCanonizingOpenHashMap<T> deepCopy(StringCanonizingOpenHashMap<T> incomingMap, Function<T, T> deepCopier) {
StringCanonizingOpenHashMap<T> newMap = new StringCanonizingOpenHashMap<>(incomingMap.size(), incomingMap.f);
ObjectIterator<Entry<String, T>> iterator = incomingMap.object2ObjectEntrySet().fastIterator();

while (iterator.hasNext()) {
Map.Entry<String, T> entry = iterator.next();
newMap.putWithoutInterning(entry.getKey(), deepCopier.apply(entry.getValue()));
final Object[] key = this.key;
for (int pos = n; pos-- != 0;) {
//noinspection ConstantValue
if (!((key[pos]) == null)) {
if (!value[pos].equals(m.get(key[pos]))) {
return false;
}
}
}

return newMap;
return true;
}
}