From b7ea7120ce3fec48288be61327fc0ce90cab299e Mon Sep 17 00:00:00 2001 From: Konstantin Ulitin Date: Fri, 15 Mar 2024 19:05:34 +0100 Subject: [PATCH] [js] optimize fast path for JSStringLiteralEscaper (WEB-65671) GitOrigin-RevId: bece292545a366588f390c67321cf9914e1bad6b --- .../json/psi/impl/JSStringLiteralEscaper.java | 61 ++++++++++++++----- 1 file changed, 46 insertions(+), 15 deletions(-) diff --git a/json/src/com/intellij/json/psi/impl/JSStringLiteralEscaper.java b/json/src/com/intellij/json/psi/impl/JSStringLiteralEscaper.java index ad4ccc04e81e..1ac133f0f546 100644 --- a/json/src/com/intellij/json/psi/impl/JSStringLiteralEscaper.java +++ b/json/src/com/intellij/json/psi/impl/JSStringLiteralEscaper.java @@ -1,23 +1,19 @@ -// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.json.psi.impl; import com.intellij.openapi.progress.ProgressManager; -import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.TextRange; import com.intellij.psi.LiteralTextEscaper; import com.intellij.psi.PsiLanguageInjectionHost; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Arrays; import java.util.stream.IntStream; public abstract class JSStringLiteralEscaper extends LiteralTextEscaper { - /** - * Offset in injected string -> offset in host string - * Last element contains imaginary offset for the character after the last one in injected string. It would be host string length. - * E.g. for "aa\nbb" it is [0,1,2,4,5,6] - */ - private int[] outSourceOffsets; + + private SourceOffsets mySourceOffsets; public JSStringLiteralEscaper(T host) { super(host); @@ -27,9 +23,9 @@ public abstract class JSStringLiteralEscaper public boolean decode(final @NotNull TextRange rangeInsideHost, @NotNull StringBuilder outChars) { String subText = rangeInsideHost.substring(myHost.getText()); - Ref sourceOffsetsRef = new Ref<>(); - boolean result = parseStringCharacters(subText, outChars, sourceOffsetsRef, isRegExpLiteral(), !isOneLine()); - outSourceOffsets = sourceOffsetsRef.get(); + SourceOffsets sourceOffsets = new SourceOffsets(); + boolean result = parseStringCharacters(subText, outChars, sourceOffsets, isRegExpLiteral(), !isOneLine()); + mySourceOffsets = sourceOffsets; return result; } @@ -37,7 +33,7 @@ public abstract class JSStringLiteralEscaper @Override public int getOffsetInHost(int offsetInDecoded, final @NotNull TextRange rangeInsideHost) { - int result = offsetInDecoded < outSourceOffsets.length ? outSourceOffsets[offsetInDecoded] : -1; + int result = mySourceOffsets.getOffsetInHost(offsetInDecoded); if (result == -1) return -1; return Math.min(result, rangeInsideHost.getLength()) + rangeInsideHost.getStartOffset(); } @@ -47,10 +43,16 @@ public abstract class JSStringLiteralEscaper return true; } - public static boolean parseStringCharacters(String chars, StringBuilder outChars, Ref sourceOffsetsRef, boolean regExp, boolean escapeBacktick) { + public static boolean parseStringCharacters(@NotNull String chars, + @NotNull StringBuilder outChars, + @Nullable SourceOffsets outSourceOffsets, + boolean regExp, + boolean escapeBacktick) { if (chars.indexOf('\\') < 0) { outChars.append(chars); - sourceOffsetsRef.set(IntStream.range(0, chars.length() + 1).toArray()); + if (outSourceOffsets != null) { + outSourceOffsets.lengthIfNoShifts = chars.length(); + } return true; } @@ -190,7 +192,36 @@ public abstract class JSStringLiteralEscaper sourceOffsets[outChars.length() - outOffset] = chars.length(); - sourceOffsetsRef.set(Arrays.copyOf(sourceOffsets, outChars.length() - outOffset + 1)); + if (outSourceOffsets != null) { + outSourceOffsets.sourceOffsets = Arrays.copyOf(sourceOffsets, outChars.length() - outOffset + 1); + } return result; } + + public static class SourceOffsets { + /** + * Offset in injected string -> offset in host string + * Last element contains imaginary offset for the character after the last one in injected string. It would be host string length. + * E.g. for "aa\nbb" it is [0,1,2,4,5,6] + */ + public int[] sourceOffsets; + /** + * Optimization for the case when all offsets in injected and host strings are the same. + */ + public int lengthIfNoShifts = -1; + + public int[] toOffsetArray() { + int[] offsets = sourceOffsets; + return offsets != null ? offsets : IntStream.range(0, lengthIfNoShifts + 1).toArray(); + } + + public int getOffsetInHost(int offsetInDecoded) { + if (lengthIfNoShifts >= 0) { + return offsetInDecoded <= lengthIfNoShifts ? offsetInDecoded : -1; + } + else { + return offsetInDecoded < sourceOffsets.length ? sourceOffsets[offsetInDecoded] : -1; + } + } + } }