Skip to content

Commit

Permalink
8302323: Add repeat methods to StringBuilder/StringBuffer
Browse files Browse the repository at this point in the history
Reviewed-by: tvaleev, redestad
  • Loading branch information
Jim Laskey committed Apr 3, 2023
1 parent dd7ca75 commit 9b9b5a7
Show file tree
Hide file tree
Showing 6 changed files with 570 additions and 4 deletions.
112 changes: 112 additions & 0 deletions src/java.base/share/classes/java/lang/AbstractStringBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
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;
Expand Down Expand Up @@ -1821,4 +1822,115 @@ private final void appendChars(CharSequence s, int off, int end) {
}
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;
}
}
30 changes: 26 additions & 4 deletions src/java.base/share/classes/java/lang/String.java
Original file line number Diff line number Diff line change
Expand Up @@ -4563,12 +4563,34 @@ public String repeat(int count) {
final int limit = len * count;
final byte[] multiple = new byte[limit];
System.arraycopy(value, 0, multiple, 0, len);
int copied = len;
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) {
System.arraycopy(multiple, 0, multiple, copied, copied);
// Power of two duplicate.
System.arraycopy(buffer, offset, buffer, offset + copied, copied);
}
System.arraycopy(multiple, 0, multiple, copied, limit - copied);
return new String(multiple, coder);
// Duplicate remainder.
System.arraycopy(buffer, offset, buffer, offset + copied, limit - copied);
}

////////////////////////////////////////////////////////////////
Expand Down
22 changes: 22 additions & 0 deletions src/java.base/share/classes/java/lang/StringBuffer.java
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,28 @@ public synchronized StringBuffer reverse() {
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() {
Expand Down
22 changes: 22 additions & 0 deletions src/java.base/share/classes/java/lang/StringBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,28 @@ public StringBuilder reverse() {
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() {
Expand Down
Loading

1 comment on commit 9b9b5a7

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.