Optimize text replacement algorithm performance

This commit is contained in:
大阔 2025-01-03 02:23:17 +08:00
parent b838d1c52a
commit 5c8086150a

View File

@ -23,7 +23,6 @@ package me.clip.placeholderapi.replacer;
import java.util.Locale; import java.util.Locale;
import java.util.function.Function; import java.util.function.Function;
import me.clip.placeholderapi.expansion.PlaceholderExpansion; import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import org.bukkit.ChatColor;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -33,6 +32,11 @@ public final class CharsReplacer implements Replacer {
@NotNull @NotNull
private final Closure closure; private final Closure closure;
// Cache StringBuilder to reduce object creation
private final ThreadLocal<StringBuilder> builderCache = ThreadLocal.withInitial(() -> new StringBuilder(256));
private final ThreadLocal<StringBuilder> identifierCache = ThreadLocal.withInitial(StringBuilder::new);
private final ThreadLocal<StringBuilder> parametersCache = ThreadLocal.withInitial(StringBuilder::new);
public CharsReplacer(@NotNull final Closure closure) { public CharsReplacer(@NotNull final Closure closure) {
this.closure = closure; this.closure = closure;
} }
@ -42,11 +46,18 @@ public final class CharsReplacer implements Replacer {
@Override @Override
public String apply(@NotNull final String text, @Nullable final OfflinePlayer player, public String apply(@NotNull final String text, @Nullable final OfflinePlayer player,
@NotNull final Function<String, @Nullable PlaceholderExpansion> lookup) { @NotNull final Function<String, @Nullable PlaceholderExpansion> lookup) {
final char[] chars = text.toCharArray(); if (text.indexOf(closure.head) == -1) { // Fast path - return directly if no placeholders
final StringBuilder builder = new StringBuilder(text.length()); return text;
}
final StringBuilder identifier = new StringBuilder(); final char[] chars = text.toCharArray();
final StringBuilder parameters = new StringBuilder(); final StringBuilder builder = builderCache.get();
final StringBuilder identifier = identifierCache.get();
final StringBuilder parameters = parametersCache.get();
try {
builder.setLength(0);
builder.ensureCapacity(text.length());
for (int i = 0; i < chars.length; i++) { for (int i = 0; i < chars.length; i++) {
final char l = chars[i]; final char l = chars[i];
@ -60,6 +71,9 @@ public final class CharsReplacer implements Replacer {
boolean invalid = true; boolean invalid = true;
boolean hadSpace = false; boolean hadSpace = false;
identifier.setLength(0);
parameters.setLength(0);
while (++i < chars.length) { while (++i < chars.length) {
final char p = chars[i]; final char p = chars[i];
@ -88,9 +102,6 @@ public final class CharsReplacer implements Replacer {
final String lowercaseIdentifierString = identifierString.toLowerCase(Locale.ROOT); final String lowercaseIdentifierString = identifierString.toLowerCase(Locale.ROOT);
final String parametersString = parameters.toString(); final String parametersString = parameters.toString();
identifier.setLength(0);
parameters.setLength(0);
if (invalid) { if (invalid) {
builder.append(closure.head).append(identifierString); builder.append(closure.head).append(identifierString);
@ -132,6 +143,12 @@ public final class CharsReplacer implements Replacer {
} }
return builder.toString(); return builder.toString();
} finally {
// Reset cached StringBuilder
builder.setLength(0);
identifier.setLength(0);
parameters.setLength(0);
}
} }
} }