Skip to content

Commit

Permalink
cleanup the code an try to make it faster (#373)
Browse files Browse the repository at this point in the history
Improve the performance of string concatenation, especially when many strings are concatenated at once.
  • Loading branch information
rbri authored and gbrail committed Jan 29, 2018
1 parent 230a542 commit c71115f
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 33 deletions.
70 changes: 37 additions & 33 deletions src/org/mozilla/javascript/ConsString.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
package org.mozilla.javascript;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.ArrayDeque;

/**
* <p>This class represents a string composed of two components, each of which
Expand All @@ -29,21 +29,15 @@ public class ConsString implements CharSequence, Serializable {

private static final long serialVersionUID = -8432806714471372570L;

private CharSequence s1, s2;
private CharSequence left, right;
private final int length;
private int depth;
private boolean isFlat;

public ConsString(CharSequence str1, CharSequence str2) {
s1 = str1;
s2 = str2;
length = str1.length() + str2.length();
depth = 1;
if (str1 instanceof ConsString) {
depth += ((ConsString)str1).depth;
}
if (str2 instanceof ConsString) {
depth += ((ConsString)str2).depth;
}
left = str1;
right = str2;
length = left.length() + right.length();
isFlat = false;
}

// Replace with string representation when serializing
Expand All @@ -53,44 +47,54 @@ private Object writeReplace() {

@Override
public String toString() {
return depth == 0 ? (String)s1 : flatten();
return isFlat ? (String)left : flatten();
}

private synchronized String flatten() {
if (depth > 0) {
StringBuilder b = new StringBuilder(length);
ArrayList<CharSequence> buffer = new ArrayList<CharSequence>();
buffer.add(s2);
buffer.add(s1);
while(!buffer.isEmpty()) {
CharSequence next = buffer.remove(buffer.size() - 1);
if (!isFlat) {
final char[] chars = new char[length];
int charPos = length;

ArrayDeque<CharSequence> stack = new ArrayDeque<CharSequence>();
stack.addFirst(left);

CharSequence next = right;
do {
if (next instanceof ConsString) {
ConsString casted = (ConsString) next;
buffer.add(casted.s2);
buffer.add(casted.s1);
} else {
b.append(next);
if (casted.isFlat) {
next = casted.left;
} else {
stack.addFirst(casted.left);
next = casted.right;
continue;
}
}
}
s1 = b.toString();
s2 = "";
depth = 0;

final String str = (String) next;
charPos -= str.length();
str.getChars(0, str.length(), chars, charPos);
next = stack.isEmpty() ? null : stack.removeFirst();
} while (next != null);

left = new String(chars);
right = "";
isFlat = true;
}
return (String)s1;
return (String)left;
}

public int length() {
return length;
}

public char charAt(int index) {
String str = depth == 0 ? (String)s1 : flatten();
String str = isFlat ? (String)left : flatten();
return str.charAt(index);
}

public CharSequence subSequence(int start, int end) {
String str = depth == 0 ? (String)s1 : flatten();
String str = isFlat ? (String)left : flatten();
return str.substring(start, end);
}

}
7 changes: 7 additions & 0 deletions testsrc/org/mozilla/javascript/tests/ConsStringTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,19 @@
import org.mozilla.javascript.ConsString;

public class ConsStringTest extends TestCase {

public void testAppend() {
ConsString current = new ConsString("a", "b");
current = new ConsString(current, "c");
current = new ConsString(current, "d");

assertEquals("abcd", current.toString());

current = new ConsString("x", new ConsString("a", "b"));
assertEquals("xab", current.toString());

current = new ConsString(new ConsString("a", "b"), new ConsString("c", "d"));
assertEquals("abcd", current.toString());
}

public void testAppendManyStrings() {
Expand Down

0 comments on commit c71115f

Please sign in to comment.