From 93508d508328d082c5892cde37d2af9ff6af82de Mon Sep 17 00:00:00 2001 From: Outfluencer Date: Wed, 4 Jun 2025 06:50:02 +1000 Subject: [PATCH] #3841, #3842: Speed up TagUtil#fromJson() Reduce complexity and recursive calls --- .../net/md_5/bungee/protocol/TagUtil.java | 94 ++++++++++++++++++- .../net/md_5/bungee/protocol/TagUtilTest.java | 13 +++ 2 files changed, 102 insertions(+), 5 deletions(-) diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/TagUtil.java b/protocol/src/main/java/net/md_5/bungee/protocol/TagUtil.java index 52b9b5cf..b942d10b 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/TagUtil.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/TagUtil.java @@ -32,6 +32,87 @@ import net.md_5.bungee.nbt.type.StringTag; public final class TagUtil { + private static byte nbtTypeOfJson(JsonElement json) + { + if ( json instanceof JsonPrimitive ) + { + JsonPrimitive jsonPrimitive = (JsonPrimitive) json; + if ( jsonPrimitive.isNumber() ) + { + Number number = json.getAsNumber(); + + if ( number instanceof Byte ) + { + return Tag.BYTE; + } else if ( number instanceof Short ) + { + return Tag.SHORT; + } else if ( number instanceof Integer ) + { + return Tag.INT; + } else if ( number instanceof Long ) + { + return Tag.LONG; + } else if ( number instanceof Float ) + { + return Tag.FLOAT; + } else if ( number instanceof Double ) + { + return Tag.DOUBLE; + } + } else if ( jsonPrimitive.isString() ) + { + return Tag.STRING; + } else if ( jsonPrimitive.isBoolean() ) + { + return Tag.BYTE; + } + throw new IllegalArgumentException( "Unknown JSON primitive: " + jsonPrimitive ); + } else if ( json instanceof JsonObject ) + { + return Tag.COMPOUND; + } else if ( json instanceof JsonArray ) + { + JsonArray array = json.getAsJsonArray(); + + Byte listType = null; + for ( JsonElement jsonEl : array ) + { + byte type = nbtTypeOfJson( jsonEl ); + if ( listType == null ) + { + listType = type; + } else if ( listType != type ) + { + listType = Tag.COMPOUND; + break; + } + } + + if ( listType == null ) + { + return Tag.LIST; + } + + switch ( listType ) + { + case Tag.BYTE: + return Tag.BYTE_ARRAY; + case Tag.INT: + return Tag.INT_ARRAY; + case Tag.LONG: + return Tag.LONG_ARRAY; + default: + return Tag.LIST; + } + } else if ( json instanceof JsonNull ) + { + return Tag.END; + } + + throw new IllegalArgumentException( "Unknown JSON element: " + json ); + } + public static TypedTag fromJson(JsonElement json) { if ( json instanceof JsonPrimitive ) @@ -66,10 +147,8 @@ public final class TagUtil } else if ( jsonPrimitive.isBoolean() ) { return new ByteTag( (byte) ( jsonPrimitive.getAsBoolean() ? 1 : 0 ) ); - } else - { - throw new IllegalArgumentException( "Unknown JSON primitive: " + jsonPrimitive ); } + throw new IllegalArgumentException( "Unknown JSON primitive: " + jsonPrimitive ); } else if ( json instanceof JsonObject ) { CompoundTag compoundTag = new CompoundTag( new LinkedHashMap<>() ); @@ -87,7 +166,7 @@ public final class TagUtil for ( JsonElement jsonEl : jsonArray ) { - byte type = fromJson( jsonEl ).getId(); + byte type = nbtTypeOfJson( jsonEl ); if ( listType == null ) { listType = type; @@ -98,8 +177,13 @@ public final class TagUtil } } - if ( listType == null ) + if ( listType == null || listType == Tag.END ) { + if ( !jsonArray.isEmpty() ) + { + throw new IllegalArgumentException( "Invalid end tag in json array: " + json ); + } + return new ListTag( Collections.emptyList(), Tag.END ); } diff --git a/protocol/src/test/java/net/md_5/bungee/protocol/TagUtilTest.java b/protocol/src/test/java/net/md_5/bungee/protocol/TagUtilTest.java index 716827da..d186f888 100644 --- a/protocol/src/test/java/net/md_5/bungee/protocol/TagUtilTest.java +++ b/protocol/src/test/java/net/md_5/bungee/protocol/TagUtilTest.java @@ -5,6 +5,7 @@ import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import com.google.gson.JsonPrimitive; import net.md_5.bungee.nbt.Tag; import net.md_5.bungee.nbt.TypedTag; @@ -224,4 +225,16 @@ public class TagUtilTest assertEquals( array.get( i ).getAsString(), string.getValue() ); } } + + @Test + public void testRecursive() + { + JsonElement jsonElement = JsonParser.parseString( "{\"extra\":[{\"extra\":[{\"extra\":[{\"color\":\"#FF0000\",\"text\":\"f\"}],\"text\":\"\"},{\"extra\":[{\"extra\":[{\"color\":\"#FF0000\",\"text\":\"f\"}],\"text\":\"\"},{\"extra\":[{\"extra\":[{\"color\":\"#FF0000\",\"text\":\"f\"}],\"text\":\"\"},{\"extra\":[{\"extra\":[{\"color\":\"#FF0000\",\"text\":\"f\"}],\"text\":\"\"},{\"extra\":[{\"extra\":[{\"color\":\"#FF0000\",\"text\":\"f\"}],\"text\":\"\"},{\"extra\":[{\"extra\":[{\"color\":\"#FF0000\",\"text\":\"f\"}],\"text\":\"\"},{\"extra\":[{\"extra\":[{\"color\":\"#FF0000\",\"text\":\"f\"}],\"text\":\"\"},{\"extra\":[{\"extra\":[{\"color\":\"#FF0000\",\"text\":\"f\"}],\"text\":\"\"},{\"extra\":[{\"extra\":[{\"color\":\"#FF0000\",\"text\":\"f\"}],\"text\":\"\"},{\"extra\":[{\"extra\":[{\"color\":\"#FF0000\",\"text\":\"f\"}],\"text\":\"\"},{\"extra\":[{\"extra\":[{\"color\":\"#FF0000\",\"text\":\"f\"}],\"text\":\"\"},{\"extra\":[{\"extra\":[{\"color\":\"#FF0000\",\"text\":\"f\"}],\"text\":\"\"},{\"extra\":[{\"extra\":[{\"color\":\"#FF0000\",\"text\":\"f\"}],\"text\":\"\"},{\"extra\":[{\"extra\":[{\"color\":\"#FF0000\",\"text\":\"f\"}],\"text\":\"\"},{\"extra\":[{\"extra\":[{\"color\":\"#FF0000\",\"text\":\"f\"}],\"text\":\"\"},{\"extra\":[{\"extra\":[{\"color\":\"#FF0000\",\"text\":\"f\"}],\"text\":\"\"},{\"extra\":[{\"extra\":[{\"color\":\"#FF0000\",\"text\":\"f\"}],\"text\":\"\"},{\"extra\":[{\"extra\":[{\"color\":\"#FF0000\",\"text\":\"f\"}],\"text\":\"\"},{\"extra\":[{\"extra\":[{\"color\":\"#FF0000\",\"text\":\"f\"}],\"text\":\"\"},{\"extra\":[{\"extra\":[{\"color\":\"#FF0000\",\"text\":\"f\"}],\"text\":\"\"},{\"extra\":[{\"extra\":[{\"color\":\"#FF0000\",\"text\":\"f\"}],\"text\":\"\"},{\"extra\":[{\"extra\":[{\"color\":\"#FF0000\",\"text\":\"f\"},{\"color\":\"#00FFFF\",\"text\":\"<\"}],\"text\":\"\"}],\"text\":\"\"}],\"text\":\"\"}],\"text\":\"\"}],\"text\":\"\"}],\"text\":\"\"}],\"text\":\"\"}],\"text\":\"\"}],\"text\":\"\"}],\"text\":\"\"}],\"text\":\"\"}],\"text\":\"\"}],\"text\":\"\"}],\"text\":\"\"}],\"text\":\"\"}],\"text\":\"\"}],\"text\":\"\"}],\"text\":\"\"}],\"text\":\"\"}],\"text\":\"\"}],\"text\":\"\"}],\"text\":\"\"}],\"text\":\"\"}],\"text\":\"\"}\n" ); + + long start = System.currentTimeMillis(); + TagUtil.fromJson( jsonElement ); + long end = System.currentTimeMillis(); + + System.out.println( "Time passed: " + ( end - start ) + "ms" ); + } }