Skip to content

Commit e83b4b2

Browse files
committedJul 23, 2024
8335182: Consolidate and streamline String concat code shapes
Reviewed-by: liach, jvernee
1 parent 4c7b3e7 commit e83b4b2

File tree

6 files changed

+452
-154
lines changed

6 files changed

+452
-154
lines changed
 

‎src/java.base/share/classes/java/lang/StringConcatHelper.java

+75-121
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -119,91 +119,64 @@ static long mix(long lengthCoder, long value) {
119119
*/
120120
static long mix(long lengthCoder, String value) {
121121
lengthCoder += value.length();
122-
if (value.coder() == String.UTF16) {
122+
if (!value.isLatin1()) {
123123
lengthCoder |= UTF16;
124124
}
125125
return checkOverflow(lengthCoder);
126126
}
127127

128128
/**
129-
* Prepends the stringly representation of boolean value into buffer,
129+
* Prepends constant and the stringly representation of value into buffer,
130130
* given the coder and final index. Index is measured in chars, not in bytes!
131131
*
132132
* @param indexCoder final char index in the buffer, along with coder packed
133133
* into higher bits.
134134
* @param buf buffer to append to
135135
* @param value boolean value to encode
136+
* @param prefix a constant to prepend before value
136137
* @return updated index (coder value retained)
137138
*/
138-
static long prepend(long indexCoder, byte[] buf, boolean value) {
139+
static long prepend(long indexCoder, byte[] buf, boolean value, String prefix) {
139140
int index = (int)indexCoder;
140141
if (indexCoder < UTF16) {
141142
if (value) {
142-
buf[--index] = 'e';
143-
buf[--index] = 'u';
144-
buf[--index] = 'r';
145-
buf[--index] = 't';
143+
index -= 4;
144+
buf[index] = 't';
145+
buf[index + 1] = 'r';
146+
buf[index + 2] = 'u';
147+
buf[index + 3] = 'e';
146148
} else {
147-
buf[--index] = 'e';
148-
buf[--index] = 's';
149-
buf[--index] = 'l';
150-
buf[--index] = 'a';
151-
buf[--index] = 'f';
149+
index -= 5;
150+
buf[index] = 'f';
151+
buf[index + 1] = 'a';
152+
buf[index + 2] = 'l';
153+
buf[index + 3] = 's';
154+
buf[index + 4] = 'e';
152155
}
156+
index -= prefix.length();
157+
prefix.getBytes(buf, index, String.LATIN1);
153158
return index;
154159
} else {
155160
if (value) {
156-
StringUTF16.putChar(buf, --index, 'e');
157-
StringUTF16.putChar(buf, --index, 'u');
158-
StringUTF16.putChar(buf, --index, 'r');
159-
StringUTF16.putChar(buf, --index, 't');
161+
index -= 4;
162+
StringUTF16.putChar(buf, index, 't');
163+
StringUTF16.putChar(buf, index + 1, 'r');
164+
StringUTF16.putChar(buf, index + 2, 'u');
165+
StringUTF16.putChar(buf, index + 3, 'e');
160166
} else {
161-
StringUTF16.putChar(buf, --index, 'e');
162-
StringUTF16.putChar(buf, --index, 's');
163-
StringUTF16.putChar(buf, --index, 'l');
164-
StringUTF16.putChar(buf, --index, 'a');
165-
StringUTF16.putChar(buf, --index, 'f');
167+
index -= 5;
168+
StringUTF16.putChar(buf, index, 'f');
169+
StringUTF16.putChar(buf, index + 1, 'a');
170+
StringUTF16.putChar(buf, index + 2, 'l');
171+
StringUTF16.putChar(buf, index + 3, 's');
172+
StringUTF16.putChar(buf, index + 4, 'e');
166173
}
174+
index -= prefix.length();
175+
prefix.getBytes(buf, index, String.UTF16);
167176
return index | UTF16;
168177
}
169178
}
170179

171-
/**
172-
* Prepends constant and the stringly representation of value into buffer,
173-
* given the coder and final index. Index is measured in chars, not in bytes!
174-
*
175-
* @param indexCoder final char index in the buffer, along with coder packed
176-
* into higher bits.
177-
* @param buf buffer to append to
178-
* @param value boolean value to encode
179-
* @param prefix a constant to prepend before value
180-
* @return updated index (coder value retained)
181-
*/
182-
static long prepend(long indexCoder, byte[] buf, boolean value, String prefix) {
183-
indexCoder = prepend(indexCoder, buf, value);
184-
indexCoder = prepend(indexCoder, buf, prefix);
185-
return indexCoder;
186-
}
187-
188-
/**
189-
* Prepends the stringly representation of char value into buffer,
190-
* given the coder and final index. Index is measured in chars, not in bytes!
191-
*
192-
* @param indexCoder final char index in the buffer, along with coder packed
193-
* into higher bits.
194-
* @param buf buffer to append to
195-
* @param value char value to encode
196-
* @return updated index (coder value retained)
197-
*/
198-
static long prepend(long indexCoder, byte[] buf, char value) {
199-
if (indexCoder < UTF16) {
200-
buf[(int)(--indexCoder)] = (byte) (value & 0xFF);
201-
} else {
202-
StringUTF16.putChar(buf, (int)(--indexCoder), value);
203-
}
204-
return indexCoder;
205-
}
206-
207180
/**
208181
* Prepends constant and the stringly representation of value into buffer,
209182
* given the coder and final index. Index is measured in chars, not in bytes!
@@ -216,26 +189,17 @@ static long prepend(long indexCoder, byte[] buf, char value) {
216189
* @return updated index (coder value retained)
217190
*/
218191
static long prepend(long indexCoder, byte[] buf, char value, String prefix) {
219-
indexCoder = prepend(indexCoder, buf, value);
220-
indexCoder = prepend(indexCoder, buf, prefix);
221-
return indexCoder;
222-
}
223-
224-
/**
225-
* Prepends the stringly representation of integer value into buffer,
226-
* given the coder and final index. Index is measured in chars, not in bytes!
227-
*
228-
* @param indexCoder final char index in the buffer, along with coder packed
229-
* into higher bits.
230-
* @param buf buffer to append to
231-
* @param value integer value to encode
232-
* @return updated index (coder value retained)
233-
*/
234-
static long prepend(long indexCoder, byte[] buf, int value) {
192+
int index = (int)indexCoder;
235193
if (indexCoder < UTF16) {
236-
return StringLatin1.getChars(value, (int)indexCoder, buf);
194+
buf[--index] = (byte) (value & 0xFF);
195+
index -= prefix.length();
196+
prefix.getBytes(buf, index, String.LATIN1);
197+
return index;
237198
} else {
238-
return StringUTF16.getChars(value, (int)indexCoder, buf) | UTF16;
199+
StringUTF16.putChar(buf, --index, value);
200+
index -= prefix.length();
201+
prefix.getBytes(buf, index, String.UTF16);
202+
return index | UTF16;
239203
}
240204
}
241205

@@ -251,26 +215,17 @@ static long prepend(long indexCoder, byte[] buf, int value) {
251215
* @return updated index (coder value retained)
252216
*/
253217
static long prepend(long indexCoder, byte[] buf, int value, String prefix) {
254-
indexCoder = prepend(indexCoder, buf, value);
255-
indexCoder = prepend(indexCoder, buf, prefix);
256-
return indexCoder;
257-
}
258-
259-
/**
260-
* Prepends the stringly representation of long value into buffer,
261-
* given the coder and final index. Index is measured in chars, not in bytes!
262-
*
263-
* @param indexCoder final char index in the buffer, along with coder packed
264-
* into higher bits.
265-
* @param buf buffer to append to
266-
* @param value long value to encode
267-
* @return updated index (coder value retained)
268-
*/
269-
static long prepend(long indexCoder, byte[] buf, long value) {
218+
int index = (int)indexCoder;
270219
if (indexCoder < UTF16) {
271-
return StringLatin1.getChars(value, (int)indexCoder, buf);
220+
index = StringLatin1.getChars(value, index, buf);
221+
index -= prefix.length();
222+
prefix.getBytes(buf, index, String.LATIN1);
223+
return index;
272224
} else {
273-
return StringUTF16.getChars(value, (int)indexCoder, buf) | UTF16;
225+
index = StringUTF16.getChars(value, index, buf);
226+
index -= prefix.length();
227+
prefix.getBytes(buf, index, String.UTF16);
228+
return index | UTF16;
274229
}
275230
}
276231

@@ -286,29 +241,18 @@ static long prepend(long indexCoder, byte[] buf, long value) {
286241
* @return updated index (coder value retained)
287242
*/
288243
static long prepend(long indexCoder, byte[] buf, long value, String prefix) {
289-
indexCoder = prepend(indexCoder, buf, value);
290-
indexCoder = prepend(indexCoder, buf, prefix);
291-
return indexCoder;
292-
}
293-
294-
/**
295-
* Prepends the stringly representation of String value into buffer,
296-
* given the coder and final index. Index is measured in chars, not in bytes!
297-
*
298-
* @param indexCoder final char index in the buffer, along with coder packed
299-
* into higher bits.
300-
* @param buf buffer to append to
301-
* @param value String value to encode
302-
* @return updated index (coder value retained)
303-
*/
304-
static long prepend(long indexCoder, byte[] buf, String value) {
305-
indexCoder -= value.length();
244+
int index = (int)indexCoder;
306245
if (indexCoder < UTF16) {
307-
value.getBytes(buf, (int)indexCoder, String.LATIN1);
246+
index = StringLatin1.getChars(value, index, buf);
247+
index -= prefix.length();
248+
prefix.getBytes(buf, index, String.LATIN1);
249+
return index;
308250
} else {
309-
value.getBytes(buf, (int)indexCoder, String.UTF16);
251+
index = StringUTF16.getChars(value, index, buf);
252+
index -= prefix.length();
253+
prefix.getBytes(buf, index, String.UTF16);
254+
return index | UTF16;
310255
}
311-
return indexCoder;
312256
}
313257

314258
/**
@@ -323,9 +267,18 @@ static long prepend(long indexCoder, byte[] buf, String value) {
323267
* @return updated index (coder value retained)
324268
*/
325269
static long prepend(long indexCoder, byte[] buf, String value, String prefix) {
326-
indexCoder = prepend(indexCoder, buf, value);
327-
indexCoder = prepend(indexCoder, buf, prefix);
328-
return indexCoder;
270+
int index = ((int)indexCoder) - value.length();
271+
if (indexCoder < UTF16) {
272+
value.getBytes(buf, index, String.LATIN1);
273+
index -= prefix.length();
274+
prefix.getBytes(buf, index, String.LATIN1);
275+
return index;
276+
} else {
277+
value.getBytes(buf, index, String.UTF16);
278+
index -= prefix.length();
279+
prefix.getBytes(buf, index, String.UTF16);
280+
return index | UTF16;
281+
}
329282
}
330283

331284
/**
@@ -375,8 +328,7 @@ static String simpleConcat(Object first, Object second) {
375328
byte[] buf = newArray(indexCoder);
376329
// prepend each argument in reverse order, since we prepending
377330
// from the end of the byte array
378-
indexCoder = prepend(indexCoder, buf, s2);
379-
indexCoder = prepend(indexCoder, buf, s1);
331+
indexCoder = prepend(indexCoder, buf, s2, s1);
380332
return newString(buf, indexCoder);
381333
}
382334

@@ -443,8 +395,10 @@ static byte[] newArrayWithSuffix(String suffix, long indexCoder) {
443395
*/
444396
@ForceInline
445397
static byte[] newArray(long indexCoder) {
446-
byte coder = (byte)(indexCoder >> 32);
447-
int index = ((int)indexCoder) << coder;
398+
int index = (int)indexCoder;
399+
if (indexCoder >= UTF16) {
400+
index <<= 1;
401+
}
448402
if (index < 0) {
449403
throw new OutOfMemoryError("Overflow: String length out of range");
450404
}

‎src/java.base/share/classes/java/lang/System.java

-4
Original file line numberDiff line numberDiff line change
@@ -2611,10 +2611,6 @@ public MethodHandle stringConcatHelper(String name, MethodType methodType) {
26112611
return StringConcatHelper.lookupStatic(name, methodType);
26122612
}
26132613

2614-
public long stringConcatHelperPrepend(long indexCoder, byte[] buf, String value) {
2615-
return StringConcatHelper.prepend(indexCoder, buf, value);
2616-
}
2617-
26182614
public long stringConcatInitialCoder() {
26192615
return StringConcatHelper.initialCoder();
26202616
}

‎src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java

+11-23
Original file line numberDiff line numberDiff line change
@@ -565,17 +565,17 @@ private static MethodHandle generateMHInlineCopy(MethodType mt, String[] constan
565565

566566
// Fold in byte[] instantiation at argument 0
567567
MethodHandle newArrayCombinator;
568-
if (suffix != null) {
569-
// newArray variant that deals with prepending any trailing constant
570-
//
571-
// initialLengthCoder is adjusted to have the correct coder
572-
// and length: The newArrayWithSuffix method expects only the coder of the
573-
// suffix to be encoded into indexCoder
574-
initialLengthCoder -= suffix.length();
575-
newArrayCombinator = newArrayWithSuffix(suffix);
576-
} else {
577-
newArrayCombinator = newArray();
568+
if (suffix == null || suffix.isEmpty()) {
569+
suffix = "";
578570
}
571+
// newArray variant that deals with prepending any trailing constant
572+
//
573+
// initialLengthCoder is adjusted to have the correct coder
574+
// and length: The newArrayWithSuffix method expects only the coder of the
575+
// suffix to be encoded into indexCoder
576+
initialLengthCoder -= suffix.length();
577+
newArrayCombinator = newArrayWithSuffix(suffix);
578+
579579
mh = MethodHandles.foldArgumentsWithCombiner(mh, 0, newArrayCombinator,
580580
1 // index
581581
);
@@ -738,9 +738,7 @@ private static MethodHandle noPrefixPrepender(Class<?> cl) {
738738
int idx = classIndex(cl);
739739
MethodHandle prepend = NO_PREFIX_PREPENDERS[idx];
740740
if (prepend == null) {
741-
NO_PREFIX_PREPENDERS[idx] = prepend = JLA.stringConcatHelper("prepend",
742-
methodType(long.class, long.class, byte[].class,
743-
Wrapper.asPrimitiveType(cl))).rebind();
741+
NO_PREFIX_PREPENDERS[idx] = prepend = MethodHandles.insertArguments(prepender(cl), 3, "");
744742
}
745743
return prepend;
746744
}
@@ -902,16 +900,6 @@ private static MethodHandle newArrayWithSuffix(String suffix) {
902900
return MethodHandles.insertArguments(mh, 0, suffix);
903901
}
904902

905-
private @Stable static MethodHandle NEW_ARRAY;
906-
private static MethodHandle newArray() {
907-
MethodHandle mh = NEW_ARRAY;
908-
if (mh == null) {
909-
NEW_ARRAY = mh =
910-
JLA.stringConcatHelper("newArray", methodType(byte[].class, long.class));
911-
}
912-
return mh;
913-
}
914-
915903
/**
916904
* Public gateways to public "stringify" methods. These methods have the
917905
* form String apply(T obj), and normally delegate to {@code String.valueOf},

‎src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java

-6
Original file line numberDiff line numberDiff line change
@@ -432,12 +432,6 @@ public interface JavaLangAccess {
432432
*/
433433
MethodHandle stringConcatHelper(String name, MethodType methodType);
434434

435-
/**
436-
* Prepends constant and the stringly representation of value into buffer,
437-
* given the coder and final index. Index is measured in chars, not in bytes!
438-
*/
439-
long stringConcatHelperPrepend(long indexCoder, byte[] buf, String value);
440-
441435
/**
442436
* Get the string concat initial coder
443437
*/

‎test/micro/org/openjdk/bench/java/lang/StringConcat.java

+12
Original file line numberDiff line numberDiff line change
@@ -219,4 +219,16 @@ public String concat23StringConst() {
219219
* questions.
220220
""";
221221
}
222+
223+
public static void main(String... args) {
224+
StringConcat concat = new StringConcat();
225+
concat.concat4String();
226+
concat.concat123String();
227+
concat.concat6String();
228+
concat.concat13String();
229+
concat.concat23String();
230+
concat.concatConstInt();
231+
}
232+
233+
222234
}

‎test/micro/org/openjdk/bench/java/lang/StringConcatStartup.java

+354
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)
Please sign in to comment.