diff --git a/src/org/mozilla/javascript/ConsString.java b/src/org/mozilla/javascript/ConsString.java
index 62cd9a8d43..d8eb4537a3 100644
--- a/src/org/mozilla/javascript/ConsString.java
+++ b/src/org/mozilla/javascript/ConsString.java
@@ -7,7 +7,7 @@
package org.mozilla.javascript;
import java.io.Serializable;
-import java.util.ArrayList;
+import java.util.ArrayDeque;
/**
*
This class represents a string composed of two components, each of which
@@ -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
@@ -53,30 +47,41 @@ 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 buffer = new ArrayList();
- 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 stack = new ArrayDeque();
+ 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() {
@@ -84,13 +89,12 @@ public int 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);
}
-
}
diff --git a/testsrc/org/mozilla/javascript/tests/ConsStringTest.java b/testsrc/org/mozilla/javascript/tests/ConsStringTest.java
index 215b0c2d39..49581f7afd 100644
--- a/testsrc/org/mozilla/javascript/tests/ConsStringTest.java
+++ b/testsrc/org/mozilla/javascript/tests/ConsStringTest.java
@@ -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() {