Skip to content

Commit

Permalink
perf: Replace Guava Splitter with faster custom String splitting method
Browse files Browse the repository at this point in the history
  • Loading branch information
sebthom committed Jun 8, 2023
1 parent 18d7088 commit 94acfc4
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@
import org.eclipse.tm4e.core.internal.grammar.tokenattrs.EncodedTokenAttributes;
import org.eclipse.tm4e.core.internal.theme.FontStyle;
import org.eclipse.tm4e.core.internal.theme.StyleAttributes;

import com.google.common.base.Splitter;
import org.eclipse.tm4e.core.internal.utils.StringUtils;

/**
* @see <a href=
Expand All @@ -37,8 +36,6 @@ public final class AttributedScopeStack {
record Frame(int encodedTokenAttributes, List<String> scopeNames) {
}

private static final Splitter BY_SPACE_SPLITTER = Splitter.on(' ');

@Nullable
static AttributedScopeStack fromExtension(final @Nullable AttributedScopeStack namesScopeList,
final List<AttributedScopeStack.Frame> contentNameScopesList) {
Expand Down Expand Up @@ -160,7 +157,7 @@ AttributedScopeStack pushAttributed(final @Nullable String scopePath, final Gram
return _pushAttributed(this, scopePath, grammar);
}

final var scopes = BY_SPACE_SPLITTER.split(scopePath);
final var scopes = StringUtils.splitToArray(scopePath, ' ');
var result = this;
for (final var scope : scopes) {
result = _pushAttributed(result, scope, grammar);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@

import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.tm4e.core.internal.grammar.ScopeStack;
import org.eclipse.tm4e.core.internal.utils.StringUtils;

import com.google.common.base.Splitter;
import com.google.common.collect.Lists;

/**
Expand All @@ -36,9 +36,6 @@
*/
public final class Theme {

private static final Splitter BY_COMMA_SPLITTER = Splitter.on(',');
private static final Splitter BY_SPACE_SPLITTER = Splitter.on(' ');

public static Theme createFromRawTheme(@Nullable final IRawTheme source, @Nullable final List<String> colorMap) {
return createFromParsedTheme(parseTheme(source), colorMap);
}
Expand Down Expand Up @@ -147,7 +144,7 @@ public static List<ParsedThemeRule> parseTheme(@Nullable final IRawTheme source)
// remove trailing commas
_scope = _scope.replaceAll(",+$", "");

scopes = BY_COMMA_SPLITTER.splitToList(_scope);
scopes = StringUtils.splitToList(_scope, ',');
} else if (settingScope instanceof List) {
@SuppressWarnings("unchecked")
final var settingScopes = (List<String>) settingScope;
Expand All @@ -161,7 +158,7 @@ public static List<ParsedThemeRule> parseTheme(@Nullable final IRawTheme source)
if (settingsFontStyle instanceof final String style) {
fontStyle = FontStyle.None;

final var segments = BY_SPACE_SPLITTER.split(style);
final var segments = StringUtils.splitToArray(style, ' ');
for (final var segment : segments) {
switch (segment) {
case "italic":
Expand Down Expand Up @@ -197,7 +194,7 @@ && isValidHexColor(stringSettingsBackground)) {
for (int j = 0, lenJ = scopes.size(); j < lenJ; j++) {
final var _scope = scopes.get(j).trim();

final var segments = BY_SPACE_SPLITTER.splitToList(_scope);
final var segments = StringUtils.splitToList(_scope, ' ');

final var scope = getLastElement(segments);
List<String> parentScopes = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@
* Contributors:
* - Microsoft Corporation: Initial code, written in TypeScript, licensed under MIT license
* - Angelo Zerr <[email protected]> - translation and adaptation to Java
* - Sebastian Thomschke - add splitToArray/List methods
*/
package org.eclipse.tm4e.core.internal.utils;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.regex.Pattern;
Expand Down Expand Up @@ -62,6 +64,59 @@ public static boolean isValidHexColor(final CharSequence hex) {
return false;
}

/**
* Very fast String splitting.
*
* 7.5 times faster than {@link String#split(String)} and 2.5 times faster than {@link com.google.common.base.Splitter}.
*/
public static String[] splitToArray(final String line, final char separator) {
var tmp = new String[8];
int count = 0;
int start = 0;
int end = line.indexOf(separator, 0);
while (end >= 0) {
if (count == tmp.length) { // check if array needs resize
final var tmp2 = new String[tmp.length + tmp.length >> 1];
System.arraycopy(tmp, 0, tmp2, 0, count);
tmp = tmp2;
}
tmp[count] = line.substring(start, end);
count++;
start = end + 1;
end = line.indexOf(separator, start);
}
if (count == tmp.length) { // check if array needs resize
final var tmp2 = new String[tmp.length + 1];
System.arraycopy(tmp, 0, tmp2, 0, count);
tmp = tmp2;
}
tmp[count] = line.substring(start);
count++;

if (count == tmp.length) {
return tmp;
}
final var result = new String[count];
System.arraycopy(tmp, 0, result, 0, count);
return result;
}

/**
* Very fast String splitting.
*/
public static List<String> splitToList(final String line, final char separator) {
final var result = new ArrayList<String>(8);
int start = 0;
int end = line.indexOf(separator, 0);
while (end >= 0) {
result.add(line.substring(start, end));
start = end + 1;
end = line.indexOf(separator, start);
}
result.add(line.substring(start));
return result;
}

public static int strcmp(final String a, final String b) {
final int result = a.compareTo(b);
if (result < 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@
import org.eclipse.tm4e.core.grammar.IGrammar;
import org.eclipse.tm4e.core.grammar.IStateStack;
import org.eclipse.tm4e.core.internal.grammar.StateStack;

import com.google.common.base.Splitter;
import org.eclipse.tm4e.core.internal.utils.StringUtils;

/**
* @see <a href=
Expand Down Expand Up @@ -140,8 +139,6 @@ private record TMTokenDecodeData(

private static final class DecodeMap {

private static final Splitter BY_DOT_SPLITTER = Splitter.on('.');

private int lastAssignedId = 0;
private final Map<String /* scope */, Integer @Nullable [] /* ids */> scopeToTokenIds = new LinkedHashMap<>();
private final Map<String /* token */, @Nullable Integer /* id */> tokenToTokenId = new LinkedHashMap<>();
Expand All @@ -153,7 +150,7 @@ Integer[] getTokenIds(final String scope) {
if (tokens != null) {
return tokens;
}
final String[] tmpTokens = BY_DOT_SPLITTER.splitToStream(scope).toArray(String[]::new);
final String[] tmpTokens = StringUtils.splitToArray(scope, '.');

tokens = new Integer[tmpTokens.length];
for (int i = 0; i < tmpTokens.length; i++) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* Copyright (c) 2023 Vegard IT GmbH and others.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Sebastian Thomschke - initial implementation
*/
package org.eclipse.tm4e.core.internal.utils;

import static org.junit.jupiter.api.Assertions.*;

import java.util.List;

import org.junit.jupiter.api.Test;

class StringUtilsTest {

@Test
void testSplitToArray() {
assertArrayEquals(new String[] { "" }, StringUtils.splitToArray("", '.'));
assertArrayEquals(new String[] { "abc" }, StringUtils.splitToArray("abc", '.'));
assertArrayEquals(new String[] { "abc", "" }, StringUtils.splitToArray("abc.", '.'));
assertArrayEquals(new String[] { "", "abc", "" }, StringUtils.splitToArray(".abc.", '.'));
assertArrayEquals(new String[] { "", "" }, StringUtils.splitToArray(".", '.'));
assertArrayEquals(new String[] { "", "", "", "" }, StringUtils.splitToArray("...", '.'));
assertArrayEquals(new String[] { "1", "2", "3", "4", "5", "6", "7", "8" },
StringUtils.splitToArray("1.2.3.4.5.6.7.8", '.'));

// test internal array resize
assertArrayEquals(new String[] { "1", "2", "3", "4", "5", "6", "7", "8", "9" },
StringUtils.splitToArray("1.2.3.4.5.6.7.8.9", '.'));
}

@Test
void testSplitToList() {
assertEquals(List.of(""), StringUtils.splitToList("", '.'));
assertEquals(List.of("abc"), StringUtils.splitToList("abc", '.'));
assertEquals(List.of("abc", ""), StringUtils.splitToList("abc.", '.'));
assertEquals(List.of("", "abc", ""), StringUtils.splitToList(".abc.", '.'));
assertEquals(List.of("", ""), StringUtils.splitToList(".", '.'));
assertEquals(List.of("", "", "", ""), StringUtils.splitToList("...", '.'));
assertEquals(List.of("1", "2", "3", "4", "5", "6", "7", "8"),
StringUtils.splitToList("1.2.3.4.5.6.7.8", '.'));

// test internal array resize
assertEquals(List.of("1", "2", "3", "4", "5", "6", "7", "8", "9"),
StringUtils.splitToList("1.2.3.4.5.6.7.8.9", '.'));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,16 @@
*/
package org.eclipse.tm4e.core.theme.css;

import org.eclipse.tm4e.core.internal.utils.StringUtils;
import org.eclipse.tm4e.core.theme.IStyle;

public final class CSSParserTest {

public static void main(final String[] args) throws Exception {
final var parser = new CSSParser(".comment {color:rgb(0,1,2)} .comment.ts {color:rgb(0,1,2)}");
String[] names = "comment".split("[.]");
parser.getBestStyle(names);
parser.getBestStyle("comment");

names = "comment.ts".split("[.]");
final IStyle style = parser.getBestStyle(names);
final IStyle style = parser.getBestStyle(StringUtils.splitToArray("comment.ts", '.'));

System.err.println(style.getColor().red);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,13 @@
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.widgets.Display;
import org.eclipse.tm4e.core.internal.utils.StringUtils;
import org.eclipse.tm4e.core.theme.RGB;
import org.eclipse.tm4e.ui.internal.utils.PreferenceUtils;
import org.eclipse.ui.texteditor.AbstractTextEditor;

import com.google.common.base.Splitter;

public final class ColorManager {

private static final Splitter BY_COMMA_SPLITTER = Splitter.on(',');

private static final ColorManager INSTANCE = new ColorManager();

public static ColorManager getInstance() {
Expand Down Expand Up @@ -132,10 +129,9 @@ private String getSystemDefaultToken(final String tokenId) {
* @return RGB value
*/
private RGB stringToRGB(final String value) {
final String[] rgbValues = BY_COMMA_SPLITTER.splitToStream(value).toArray(String[]::new);
final String[] rgbValues = StringUtils.splitToArray(value, ',');
return rgbValues.length == 3
? new RGB(Integer.parseInt(rgbValues[0]), Integer.parseInt(rgbValues[1]),
Integer.parseInt(rgbValues[2]))
? new RGB(Integer.parseInt(rgbValues[0]), Integer.parseInt(rgbValues[1]), Integer.parseInt(rgbValues[2]))
: new RGB(255, 255, 255);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,16 @@
import org.eclipse.jface.text.rules.Token;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.tm4e.core.internal.utils.StringUtils;
import org.eclipse.tm4e.core.theme.IStyle;
import org.eclipse.tm4e.core.theme.RGB;
import org.eclipse.tm4e.core.theme.css.CSSParser;
import org.eclipse.tm4e.ui.TMUIPlugin;
import org.eclipse.tm4e.ui.themes.AbstractTokenProvider;
import org.eclipse.tm4e.ui.themes.ColorManager;

import com.google.common.base.Splitter;

public class CSSTokenProvider extends AbstractTokenProvider {

private static final Splitter BY_DOT_SPLITTER = Splitter.on('.');

private static class NoopCSSParser extends CSSParser {
@Override
public List<IStyle> getStyles() {
Expand Down Expand Up @@ -89,7 +86,7 @@ public IToken getToken(@Nullable final String type) {
if (type == null)
return null;

final IStyle style = parser.getBestStyle(BY_DOT_SPLITTER.splitToStream(type).toArray(String[]::new));
final IStyle style = parser.getBestStyle(StringUtils.splitToArray(type, '.'));
if (style == null)
return null;

Expand Down

0 comments on commit 94acfc4

Please sign in to comment.