Skip to content

Commit 12d584f

Browse files
committed
LazyString implementation
1 parent d6cd2f0 commit 12d584f

File tree

6 files changed

+346
-7
lines changed

6 files changed

+346
-7
lines changed

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ImpModuleBuiltins.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
import com.oracle.graal.python.builtins.objects.function.PArguments;
6464
import com.oracle.graal.python.builtins.objects.module.PythonModule;
6565
import com.oracle.graal.python.builtins.objects.object.PythonObject;
66+
import com.oracle.graal.python.builtins.objects.str.PString;
6667
import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode;
6768
import com.oracle.graal.python.nodes.call.special.CallUnaryMethodNode;
6869
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
@@ -342,6 +343,11 @@ public abstract static class TruffleImportStar extends PythonBuiltinNode {
342343
public Object run(String path, String modulename) {
343344
return run(path, getCore().lookupBuiltinModule(modulename));
344345
}
346+
347+
@Specialization
348+
public Object run(PString path, String modulename) {
349+
return run(path.getValue(), getCore().lookupBuiltinModule(modulename));
350+
}
345351

346352
@Specialization
347353
@TruffleBoundary
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
/*
2+
* To change this license header, choose License Headers in Project Properties.
3+
* To change this template file, choose Tools | Templates
4+
* and open the template in the editor.
5+
*/
6+
package com.oracle.graal.python.builtins.objects.str;
7+
8+
import com.oracle.graal.python.nodes.PGuards;
9+
import com.oracle.graal.python.runtime.PythonOptions;
10+
import com.oracle.truffle.api.CompilerAsserts;
11+
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
12+
import com.oracle.truffle.api.profiles.ConditionProfile;
13+
14+
public class LazyString implements CharSequence {
15+
16+
protected static int MinLazyStringLength = PythonOptions.getMinLazyStringLength();
17+
protected static boolean UseLazyStrings = PythonOptions.useLazyString();
18+
19+
public static int length(CharSequence cs, ConditionProfile profile1, ConditionProfile profile2) {
20+
if (profile1.profile(cs instanceof String)) {
21+
return ((String) cs).length();
22+
} else if (profile2.profile(cs instanceof LazyString)) {
23+
return ((LazyString) cs).length();
24+
}
25+
return lengthIntl(cs);
26+
}
27+
28+
@TruffleBoundary
29+
private static int lengthIntl(CharSequence cs) {
30+
return cs.length();
31+
}
32+
33+
@TruffleBoundary
34+
public static CharSequence create(CharSequence left, CharSequence right) {
35+
assert PGuards.isString(left);
36+
assert PGuards.isString(right);
37+
if (UseLazyStrings) {
38+
if (left.length() == 0) {
39+
return right;
40+
} else if (right.length() == 0) {
41+
return left;
42+
}
43+
int resultLength = left.length() + right.length();
44+
if (resultLength < MinLazyStringLength) {
45+
return left.toString() + right.toString();
46+
}
47+
return new LazyString(left, right, resultLength);
48+
} else {
49+
return left.toString() + right.toString();
50+
}
51+
}
52+
53+
/**
54+
* Only use when invariants are checked already, e.g. from specializing nodes.
55+
*/
56+
@TruffleBoundary
57+
public static CharSequence createChecked(CharSequence left, CharSequence right, int length) {
58+
assert assertChecked(left, right, length);
59+
return new LazyString(left, right, length);
60+
}
61+
62+
private static boolean assertChecked(CharSequence left, CharSequence right, int length) {
63+
assert UseLazyStrings;
64+
assert (PGuards.isString(left) || left instanceof LazyString) && (PGuards.isString(right) || right instanceof LazyString);
65+
assert length == left.length() + right.length();
66+
assert left.length() > 0 && right.length() > 0;
67+
assert length >= MinLazyStringLength;
68+
return true;
69+
}
70+
71+
/**
72+
* Variant of {@link #createChecked} that tries to concatenate a very short string to an already
73+
* short root leaf up-front, e.g. when appending single characters.
74+
*/
75+
@TruffleBoundary
76+
public static CharSequence createCheckedShort(CharSequence left, CharSequence right, int length) {
77+
assertChecked(left, right, length);
78+
final int tinyLimit = 1;
79+
final int appendToLeafLimit = MinLazyStringLength / 2;
80+
if (left instanceof LazyString && right instanceof String && right.length() <= tinyLimit) {
81+
CharSequence ll = ((LazyString) left).left;
82+
CharSequence lr = ((LazyString) left).right;
83+
if (lr != null && lr instanceof String && lr.length() + right.length() <= appendToLeafLimit) {
84+
return new LazyString(ll, lr.toString() + right.toString(), length);
85+
}
86+
} else if (left instanceof String && left.length() <= tinyLimit && right instanceof LazyString) {
87+
CharSequence ll = ((LazyString) right).left;
88+
CharSequence lr = ((LazyString) right).right;
89+
if (lr != null && ll instanceof String && left.length() + ll.length() <= appendToLeafLimit) {
90+
return new LazyString(left.toString() + ll.toString(), lr, length);
91+
}
92+
}
93+
return new LazyString(left, right, length);
94+
}
95+
96+
private CharSequence left;
97+
private CharSequence right;
98+
private final int length;
99+
100+
private LazyString(CharSequence left, CharSequence right, int length) {
101+
assert left.length() > 0 && right.length() > 0 && length == left.length() + right.length();
102+
this.left = left;
103+
this.right = right;
104+
this.length = length;
105+
}
106+
107+
private LazyString(CharSequence left, CharSequence right) {
108+
this(left, right, left.length() + right.length());
109+
}
110+
111+
@Override
112+
public int length() {
113+
return length;
114+
}
115+
116+
@Override
117+
public String toString() {
118+
if (!isFlat()) {
119+
flatten();
120+
}
121+
return (String) left;
122+
}
123+
124+
private boolean isFlat() {
125+
return right == null;
126+
}
127+
128+
@TruffleBoundary
129+
private void flatten() {
130+
char[] dst = new char[length];
131+
flatten(this, 0, length, dst, 0);
132+
left = new String(dst);
133+
right = null;
134+
}
135+
136+
private static void flatten(CharSequence src, int srcBegin, int srcEnd, char[] dst, int dstBegin) {
137+
CompilerAsserts.neverPartOfCompilation();
138+
CharSequence str = src;
139+
int from = srcBegin;
140+
int to = srcEnd;
141+
int dstFrom = dstBegin;
142+
for (;;) {
143+
assert 0 <= from && from <= to && to <= str.length();
144+
if (str instanceof LazyString) {
145+
LazyString lazyString = (LazyString) str;
146+
CharSequence left = lazyString.left;
147+
CharSequence right = lazyString.right;
148+
int mid = left.length();
149+
150+
if (to - mid >= mid - from) {
151+
// right is longer, recurse left
152+
if (from < mid) {
153+
if (left instanceof String) {
154+
((String) left).getChars(from, mid, dst, dstFrom);
155+
} else {
156+
flatten(left, from, mid, dst, dstFrom);
157+
}
158+
dstFrom += mid - from;
159+
from = 0;
160+
} else {
161+
from -= mid;
162+
}
163+
to -= mid;
164+
str = right;
165+
} else {
166+
// left is longer, recurse right
167+
if (to > mid) {
168+
if (right instanceof String) {
169+
((String) right).getChars(0, to - mid, dst, dstFrom + mid - from);
170+
} else {
171+
flatten(right, 0, to - mid, dst, dstFrom + mid - from);
172+
}
173+
to = mid;
174+
}
175+
str = left;
176+
}
177+
} else if (str instanceof String) {
178+
((String) str).getChars(from, to, dst, dstFrom);
179+
return;
180+
}
181+
}
182+
}
183+
184+
@Override
185+
public char charAt(int index) {
186+
return toString().charAt(index);
187+
}
188+
189+
@Override
190+
public CharSequence subSequence(int start, int end) {
191+
return toString().subSequence(start, end);
192+
}
193+
194+
public boolean isEmpty() {
195+
return length == 0;
196+
}
197+
198+
// accessed via Java Interop, JDK-8062624.js
199+
@TruffleBoundary
200+
public boolean startsWith(String prefix) {
201+
return toString().startsWith(prefix);
202+
}
203+
204+
// accessed via Java Interop, JDK-8062624.js
205+
@TruffleBoundary
206+
public boolean endsWith(String prefix) {
207+
return toString().endsWith(prefix);
208+
}
209+
210+
// accessed via Java Interop, JDK-8062624.js
211+
@TruffleBoundary
212+
public byte[] getBytes() {
213+
return toString().getBytes();
214+
}
215+
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/PString.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,18 @@
3333

3434
public final class PString extends PImmutableSequence {
3535

36-
private final String value;
36+
private final CharSequence value;
3737

38-
public PString(PythonClass clazz, String value) {
38+
public PString(PythonClass clazz, CharSequence value) {
3939
super(clazz);
4040
this.value = value;
4141
}
4242

4343
public String getValue() {
44+
return value.toString();
45+
}
46+
47+
public CharSequence getCharSequence() {
4448
return value;
4549
}
4650

@@ -66,7 +70,7 @@ public boolean lessThan(PSequence sequence) {
6670

6771
@Override
6872
public String toString() {
69-
return value;
73+
return value.toString();
7074
}
7175

7276
@Override
@@ -82,6 +86,9 @@ public int index(Object value) {
8286

8387
@Override
8488
public int hashCode() {
89+
if (value instanceof LazyString) {
90+
return value.toString().hashCode();
91+
}
8592
return value.hashCode();
8693
}
8794

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringBuiltins.java

Lines changed: 92 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -312,23 +312,111 @@ PNotImplemented doGeneric(Object self, Object other) {
312312

313313
@Builtin(name = __ADD__, fixedNumOfArguments = 2)
314314
@GenerateNodeFactory
315-
@TypeSystemReference(PythonArithmeticTypes.class)
316315
public abstract static class AddNode extends PythonBinaryBuiltinNode {
317-
@Specialization
318-
String doSS(String self, String other) {
319-
return new StringBuilder(self.length() + other.length()).append(self).append(other).toString();
316+
317+
protected final ConditionProfile leftProfile1 = ConditionProfile.createBinaryProfile();
318+
protected final ConditionProfile leftProfile2 = ConditionProfile.createBinaryProfile();
319+
protected final ConditionProfile rightProfile1 = ConditionProfile.createBinaryProfile();
320+
protected final ConditionProfile rightProfile2 = ConditionProfile.createBinaryProfile();
321+
322+
@Specialization (guards="!concatGuard(self, other)")
323+
String doSSSimple(String self, String other) {
324+
if (LazyString.length(self, leftProfile1, leftProfile2) == 0) {
325+
return other;
326+
}
327+
return self;
328+
}
329+
330+
@Specialization (guards="!concatGuard(self, other.getCharSequence())")
331+
Object doSSSimple(String self, PString other) {
332+
if (LazyString.length(self, leftProfile1, leftProfile2) == 0) {
333+
return other;
334+
}
335+
return self;
336+
}
337+
338+
@Specialization (guards="!concatGuard(self.getCharSequence(), other)")
339+
Object doSSSimple(PString self, String other) {
340+
if (LazyString.length(self.getCharSequence(), leftProfile1, leftProfile2) == 0) {
341+
return other;
342+
}
343+
return self;
344+
}
345+
346+
@Specialization (guards="!concatGuard(self.getCharSequence(), self.getCharSequence())")
347+
PString doSSSimple(PString self, PString other) {
348+
if (LazyString.length(self.getCharSequence(), leftProfile1, leftProfile2) == 0) {
349+
return other;
350+
}
351+
return self;
352+
}
353+
354+
@Specialization (guards="concatGuard(self.getCharSequence(), other)")
355+
Object doSS(PString self, String other,
356+
@Cached("createBinaryProfile()") ConditionProfile shortStringAppend) {
357+
return doIt(self.getCharSequence(), other, shortStringAppend);
358+
}
359+
360+
@Specialization (guards="concatGuard(self, other)")
361+
Object doSS(String self, String other,
362+
@Cached("createBinaryProfile()") ConditionProfile shortStringAppend) {
363+
return doIt(self, other, shortStringAppend);
364+
}
365+
366+
@Specialization (guards="concatGuard(self, other.getCharSequence())")
367+
Object doSS(String self, PString other,
368+
@Cached("createBinaryProfile()") ConditionProfile shortStringAppend) {
369+
return doIt(self, other.getCharSequence(), shortStringAppend);
370+
}
371+
372+
@Specialization (guards="concatGuard(self.getCharSequence(), other.getCharSequence())")
373+
Object doSS(PString self, PString other,
374+
@Cached("createBinaryProfile()") ConditionProfile shortStringAppend) {
375+
return doIt(self.getCharSequence(), other.getCharSequence(), shortStringAppend);
376+
}
377+
378+
private Object doIt(CharSequence left, CharSequence right, ConditionProfile shortStringAppend) {
379+
if (LazyString.UseLazyStrings) {
380+
int leftLength = LazyString.length(left, leftProfile1, leftProfile2);
381+
int rightLength = LazyString.length(right, rightProfile1, rightProfile2);
382+
int resultLength = leftLength + rightLength;
383+
if (resultLength >= LazyString.MinLazyStringLength) {
384+
if (shortStringAppend.profile(leftLength == 1 || rightLength == 1)) {
385+
return factory().createString(LazyString.createCheckedShort(left, right, resultLength));
386+
} else {
387+
return factory().createString(LazyString.createChecked(left, right, resultLength));
388+
}
389+
}
390+
}
391+
return stringConcat(left, right);
392+
}
393+
394+
@TruffleBoundary
395+
private static String stringConcat(CharSequence left, CharSequence right) {
396+
return left.toString() + right.toString();
320397
}
321398

322399
@Specialization(guards = "!isString(other)")
323400
String doSO(@SuppressWarnings("unused") String self, Object other) {
324401
throw raise(TypeError, "Can't convert '%p' object to str implicitly", other);
325402
}
403+
404+
@Specialization(guards = "!isString(other)")
405+
String doSO(@SuppressWarnings("unused") PString self, Object other) {
406+
throw raise(TypeError, "Can't convert '%p' object to str implicitly", other);
407+
}
326408

327409
@SuppressWarnings("unused")
328410
@Fallback
329411
PNotImplemented doGeneric(Object self, Object other) {
330412
return PNotImplemented.NOT_IMPLEMENTED;
331413
}
414+
415+
protected boolean concatGuard(CharSequence left, CharSequence right) {
416+
int leftLength = LazyString.length(left, leftProfile1, leftProfile2);
417+
int rightLength = LazyString.length(right, rightProfile1, rightProfile2);
418+
return leftLength > 0 && rightLength > 0;
419+
}
332420
}
333421

334422
@Builtin(name = __RADD__, fixedNumOfArguments = 2)

0 commit comments

Comments
 (0)