From 5c8086150ae35aad64a2001a37ebd2813377f29e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A7=E9=98=94?= <1520622465@qq.com> Date: Fri, 3 Jan 2025 02:23:17 +0800 Subject: [PATCH] Optimize text replacement algorithm performance --- .../replacer/CharsReplacer.java | 175 ++++++++++-------- 1 file changed, 96 insertions(+), 79 deletions(-) diff --git a/src/main/java/me/clip/placeholderapi/replacer/CharsReplacer.java b/src/main/java/me/clip/placeholderapi/replacer/CharsReplacer.java index fe08457..c5ad7b1 100644 --- a/src/main/java/me/clip/placeholderapi/replacer/CharsReplacer.java +++ b/src/main/java/me/clip/placeholderapi/replacer/CharsReplacer.java @@ -23,7 +23,6 @@ package me.clip.placeholderapi.replacer; import java.util.Locale; import java.util.function.Function; import me.clip.placeholderapi.expansion.PlaceholderExpansion; -import org.bukkit.ChatColor; import org.bukkit.OfflinePlayer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -32,6 +31,11 @@ public final class CharsReplacer implements Replacer { @NotNull private final Closure closure; + + // Cache StringBuilder to reduce object creation + private final ThreadLocal builderCache = ThreadLocal.withInitial(() -> new StringBuilder(256)); + private final ThreadLocal identifierCache = ThreadLocal.withInitial(StringBuilder::new); + private final ThreadLocal parametersCache = ThreadLocal.withInitial(StringBuilder::new); public CharsReplacer(@NotNull final Closure closure) { this.closure = closure; @@ -42,96 +46,109 @@ public final class CharsReplacer implements Replacer { @Override public String apply(@NotNull final String text, @Nullable final OfflinePlayer player, @NotNull final Function lookup) { + if (text.indexOf(closure.head) == -1) { // Fast path - return directly if no placeholders + return text; + } + final char[] chars = text.toCharArray(); - final StringBuilder builder = new StringBuilder(text.length()); + final StringBuilder builder = builderCache.get(); + final StringBuilder identifier = identifierCache.get(); + final StringBuilder parameters = parametersCache.get(); + + try { + builder.setLength(0); + builder.ensureCapacity(text.length()); - final StringBuilder identifier = new StringBuilder(); - final StringBuilder parameters = new StringBuilder(); + for (int i = 0; i < chars.length; i++) { + final char l = chars[i]; - for (int i = 0; i < chars.length; i++) { - final char l = chars[i]; - - if (l != closure.head || i + 1 >= chars.length) { - builder.append(l); - continue; - } - - boolean identified = false; - boolean invalid = true; - boolean hadSpace = false; - - while (++i < chars.length) { - final char p = chars[i]; - - if (p == ' ' && !identified) { - hadSpace = true; - break; - } - if (p == closure.tail) { - invalid = false; - break; - } - - if (p == '_' && !identified) { - identified = true; + if (l != closure.head || i + 1 >= chars.length) { + builder.append(l); continue; } - if (identified) { - parameters.append(p); - } else { - identifier.append(p); + boolean identified = false; + boolean invalid = true; + boolean hadSpace = false; + + identifier.setLength(0); + parameters.setLength(0); + + while (++i < chars.length) { + final char p = chars[i]; + + if (p == ' ' && !identified) { + hadSpace = true; + break; + } + if (p == closure.tail) { + invalid = false; + break; + } + + if (p == '_' && !identified) { + identified = true; + continue; + } + + if (identified) { + parameters.append(p); + } else { + identifier.append(p); + } } + + final String identifierString = identifier.toString(); + final String lowercaseIdentifierString = identifierString.toLowerCase(Locale.ROOT); + final String parametersString = parameters.toString(); + + if (invalid) { + builder.append(closure.head).append(identifierString); + + if (identified) { + builder.append('_').append(parametersString); + } + + if (hadSpace) { + builder.append(' '); + } + continue; + } + + final PlaceholderExpansion placeholder = lookup.apply(lowercaseIdentifierString); + if (placeholder == null) { + builder.append(closure.head).append(identifierString); + + if (identified) { + builder.append('_'); + } + + builder.append(parametersString).append(closure.tail); + continue; + } + + final String replacement = placeholder.onRequest(player, parametersString); + if (replacement == null) { + builder.append(closure.head).append(identifierString); + + if (identified) { + builder.append('_'); + } + + builder.append(parametersString).append(closure.tail); + continue; + } + + builder.append(replacement); } - final String identifierString = identifier.toString(); - final String lowercaseIdentifierString = identifierString.toLowerCase(Locale.ROOT); - final String parametersString = parameters.toString(); - + return builder.toString(); + } finally { + // Reset cached StringBuilder + builder.setLength(0); identifier.setLength(0); parameters.setLength(0); - - if (invalid) { - builder.append(closure.head).append(identifierString); - - if (identified) { - builder.append('_').append(parametersString); - } - - if (hadSpace) { - builder.append(' '); - } - continue; - } - - final PlaceholderExpansion placeholder = lookup.apply(lowercaseIdentifierString); - if (placeholder == null) { - builder.append(closure.head).append(identifierString); - - if (identified) { - builder.append('_'); - } - - builder.append(parametersString).append(closure.tail); - continue; - } - - final String replacement = placeholder.onRequest(player, parametersString); - if (replacement == null) { - builder.append(closure.head).append(identifierString); - - if (identified) { - builder.append('_'); - } - - builder.append(parametersString).append(closure.tail); - continue; - } - - builder.append(replacement); } - - return builder.toString(); } }