8302323: Add repeat methods to StringBuilder/StringBuffer

Reviewed-by: tvaleev, redestad
This commit is contained in:
Jim Laskey 2023-04-03 15:29:21 +00:00
parent dd7ca757a7
commit 9b9b5a7a5c
6 changed files with 571 additions and 5 deletions

View file

@ -29,6 +29,7 @@ import jdk.internal.math.DoubleToDecimal;
import jdk.internal.math.FloatToDecimal;
import java.io.IOException;
import java.nio.CharBuffer;
import java.util.Arrays;
import java.util.Spliterator;
import java.util.stream.IntStream;
@ -1821,4 +1822,115 @@ abstract sealed class AbstractStringBuilder implements Appendable, CharSequence
}
count += end - off;
}
private AbstractStringBuilder repeat(char c, int count) {
int limit = this.count + count;
ensureCapacityInternal(limit);
boolean isLatin1 = isLatin1();
if (isLatin1 && StringLatin1.canEncode(c)) {
Arrays.fill(value, this.count, limit, (byte)c);
} else {
if (isLatin1) {
inflate();
}
for (int index = this.count; index < limit; index++) {
StringUTF16.putCharSB(value, index, c);
}
}
this.count = limit;
return this;
}
/**
* Repeats {@code count} copies of the string representation of the
* {@code codePoint} argument to this sequence.
* <p>
* The length of this sequence increases by {@code count} times the
* string representation length.
* <p>
* It is usual to use {@code char} expressions for code points. For example:
* {@snippet lang="java":
* // insert 10 asterisks into the buffer
* sb.repeat('*', 10);
* }
*
* @param codePoint code point to append
* @param count number of times to copy
*
* @return a reference to this object.
*
* @throws IllegalArgumentException if the specified {@code codePoint}
* is not a valid Unicode code point or if {@code count} is negative.
*
* @since 21
*/
public AbstractStringBuilder repeat(int codePoint, int count) {
if (count < 0) {
throw new IllegalArgumentException("count is negative: " + count);
} else if (count == 0) {
return this;
}
if (Character.isBmpCodePoint(codePoint)) {
repeat((char)codePoint, count);
} else {
repeat(CharBuffer.wrap(Character.toChars(codePoint)), count);
}
return this;
}
/**
* Appends {@code count} copies of the specified {@code CharSequence} {@code cs}
* to this sequence.
* <p>
* The length of this sequence increases by {@code count} times the
* {@code CharSequence} length.
* <p>
* If {@code cs} is {@code null}, then the four characters
* {@code "null"} are repeated into this sequence.
*
* @param cs a {@code CharSequence}
* @param count number of times to copy
*
* @return a reference to this object.
*
* @throws IllegalArgumentException if {@code count} is negative
*
* @since 21
*/
public AbstractStringBuilder repeat(CharSequence cs, int count) {
if (count < 0) {
throw new IllegalArgumentException("count is negative: " + count);
} else if (count == 0) {
return this;
} else if (count == 1) {
return append(cs);
}
if (cs == null) {
cs = "null";
}
int length = cs.length();
if (length == 0) {
return this;
} else if (length == 1) {
return repeat(cs.charAt(0), count);
}
int offset = this.count;
int valueLength = length << coder;
if ((Integer.MAX_VALUE - offset) / count < valueLength) {
throw new OutOfMemoryError("Required length exceeds implementation limit");
}
int total = count * length;
int limit = offset + total;
ensureCapacityInternal(limit);
if (cs instanceof String str) {
putStringAt(offset, str);
} else if (cs instanceof AbstractStringBuilder asb) {
append(asb);
} else {
appendChars(cs, 0, length);
}
String.repeatCopyRest(value, offset << coder, total << coder, length << coder);
this.count = limit;
return this;
}
}

View file

@ -4563,14 +4563,36 @@ public final class String
final int limit = len * count;
final byte[] multiple = new byte[limit];
System.arraycopy(value, 0, multiple, 0, len);
int copied = len;
for (; copied < limit - copied; copied <<= 1) {
System.arraycopy(multiple, 0, multiple, copied, copied);
}
System.arraycopy(multiple, 0, multiple, copied, limit - copied);
repeatCopyRest(multiple, 0, limit, len);
return new String(multiple, coder);
}
/**
* Used to perform copying after the initial insertion. Copying is optimized
* by using power of two duplication. First pass duplicates original copy,
* second pass then duplicates the original and the copy yielding four copies,
* third pass duplicates four copies yielding eight copies, and so on.
* Finally, the remainder is filled in with prior copies.
*
* @implNote The technique used here is significantly faster than hand-rolled
* loops or special casing small numbers due to the intensive optimization
* done by intrinsic {@code System.arraycopy}.
*
* @param buffer destination buffer
* @param offset offset in the destination buffer
* @param limit total replicated including what is already in the buffer
* @param copied number of bytes that have already in the buffer
*/
static void repeatCopyRest(byte[] buffer, int offset, int limit, int copied) {
// Initial copy is in the buffer.
for (; copied < limit - copied; copied <<= 1) {
// Power of two duplicate.
System.arraycopy(buffer, offset, buffer, offset + copied, copied);
}
// Duplicate remainder.
System.arraycopy(buffer, offset, buffer, offset + copied, limit - copied);
}
////////////////////////////////////////////////////////////////
/**

View file

@ -708,6 +708,28 @@ import jdk.internal.vm.annotation.IntrinsicCandidate;
return this;
}
/**
* @throws IllegalArgumentException {@inheritDoc}
*
* @since 21
*/
@Override
public synchronized StringBuffer repeat(int codePoint, int count) {
super.repeat(codePoint, count);
return this;
}
/**
* @throws IllegalArgumentException {@inheritDoc}
*
* @since 21
*/
@Override
public synchronized StringBuffer repeat(CharSequence cs, int count) {
super.repeat(cs, count);
return this;
}
@Override
@IntrinsicCandidate
public synchronized String toString() {

View file

@ -446,6 +446,28 @@ public final class StringBuilder
return this;
}
/**
* @throws IllegalArgumentException {@inheritDoc}
*
* @since 21
*/
@Override
public StringBuilder repeat(int codePoint, int count) {
super.repeat(codePoint, count);
return this;
}
/**
* @throws IllegalArgumentException {@inheritDoc}
*
* @since 21
*/
@Override
public StringBuilder repeat(CharSequence cs, int count) {
super.repeat(cs, count);
return this;
}
@Override
@IntrinsicCandidate
public String toString() {