This is an automated email from the ASF dual-hosted git repository.
garydgregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-lang.git
The following commit(s) were added to refs/heads/master by this push:
new 2dfaafa25 FormattableUtils.append pads right-justified output with
insert(0) per char; O(width^2); '%500000s' costs ~1.25e11 char moves (f024).
2dfaafa25 is described below
commit 2dfaafa2576afc5534575fa27967b622e86580b9
Author: Gary Gregory <[email protected]>
AuthorDate: Sat Sep 5 15:20:02 2026 -0400
FormattableUtils.append pads right-justified output with insert(0) per
char; O(width^2); '%500000s' costs ~1.25e11 char moves (f024).
---
src/changes/changes.xml | 1 +
.../commons/lang3/text/FormattableUtils.java | 5 ++--
.../commons/lang3/text/FormattableUtilsTest.java | 35 ++++++++++++++++++++++
3 files changed, 39 insertions(+), 2 deletions(-)
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index d6c2f9a74..228772b45 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -273,6 +273,7 @@ java.lang.NullPointerException: Cannot invoke
<action type="fix" dev="ggregory" due-to="Gary
Gregory">Fraction.getFraction(String) throws undeclared ArithmeticException on
crafted numeric strings ('9999999999.5', '1/0', mixed-number overflow)
(f021).</action>
<action type="fix" dev="ggregory" due-to="Gary
Gregory">StringEscapeUtils.escapeHtml4/3 apostrophe gap: single-quoted and
unquoted HTML attribute contexts are trivially breakable, and the javadoc
discloses the gap only as an HTML4 entity-legality footnote (f022).</action>
<action type="fix" dev="ggregory" due-to="Gary
Gregory">ArrayUtils.addAll/insert length arithmetic overflows (undeclared
NegativeArraySizeException) while sibling concat is overflow-checked
(f023).</action>
+ <action type="fix" dev="ggregory" due-to="Gary
Gregory">FormattableUtils.append pads right-justified output with insert(0) per
char; O(width^2); '%500000s' costs ~1.25e11 char moves (f024).</action>
<!-- ADD -->
<action type="add" dev="ggregory" due-to="Gary
Gregory">Add JavaVersion.JAVA_27.</action>
<action type="add" dev="ggregory" due-to="Gary
Gregory">Add SystemUtils.IS_JAVA_27.</action>
diff --git a/src/main/java/org/apache/commons/lang3/text/FormattableUtils.java
b/src/main/java/org/apache/commons/lang3/text/FormattableUtils.java
index 3837a4e88..11bb49bea 100644
--- a/src/main/java/org/apache/commons/lang3/text/FormattableUtils.java
+++ b/src/main/java/org/apache/commons/lang3/text/FormattableUtils.java
@@ -101,8 +101,9 @@ public static Formatter append(final CharSequence seq,
final Formatter formatter
buf.replace(precision - actualEllipsis.length(), seq.length(),
actualEllipsis.toString());
}
final boolean leftJustify = (flags & FormattableFlags.LEFT_JUSTIFY) ==
FormattableFlags.LEFT_JUSTIFY;
- for (int i = buf.length(); i < width; i++) {
- buf.insert(leftJustify ? i : 0, padChar);
+ if (width > buf.length()) {
+ final String padding = StringUtils.repeat(padChar, width -
buf.length());
+ buf.insert(leftJustify ? buf.length() : 0, padding);
}
return formatter.format(SIMPLEST_FORMAT, buf.toString());
}
diff --git
a/src/test/java/org/apache/commons/lang3/text/FormattableUtilsTest.java
b/src/test/java/org/apache/commons/lang3/text/FormattableUtilsTest.java
index 9bc896c34..592bd51e1 100644
--- a/src/test/java/org/apache/commons/lang3/text/FormattableUtilsTest.java
+++ b/src/test/java/org/apache/commons/lang3/text/FormattableUtilsTest.java
@@ -23,7 +23,10 @@
import java.util.Formatter;
import org.apache.commons.lang3.AbstractLangTest;
+import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
/**
* Tests {@link FormattableUtils}.
@@ -110,11 +113,43 @@ void testEllipsis() {
assertEquals("+* ", FormattableUtils.append("foo", new Formatter(),
FormattableFlags.LEFT_JUSTIFY, 5, 2, "+*").toString());
}
+ @ParameterizedTest
+ @ValueSource(ints = {0, FormattableFlags.LEFT_JUSTIFY})
+ void testEmptyPadding(final int flags) {
+ assertEquals("_____", FormattableUtils.append("", new Formatter(),
flags, 5, -1, '_').toString());
+ assertEquals("_____", FormattableUtils.append("foo", new Formatter(),
flags, 5, 0, '_').toString());
+ }
+
@Test
void testIllegalEllipsis() {
assertIllegalArgumentException(() -> FormattableUtils.append("foo",
new Formatter(), 0, -1, 1, "xx"));
}
+ @ParameterizedTest
+ @ValueSource(ints = {0, FormattableFlags.LEFT_JUSTIFY})
+ void testLargePadding(final int flags) {
+ final int width = 500_000;
+ final String padding = StringUtils.repeat(' ', width - 3);
+ final String expected = flags == 0 ? padding + "foo" : "foo" + padding;
+ assertEquals(expected, FormattableUtils.append("foo", new Formatter(),
flags, width, -1).toString());
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = {0, FormattableFlags.LEFT_JUSTIFY})
+ void testLargePaddingWithEllipsis(final int flags) {
+ final int width = 500_000;
+ final String padding = StringUtils.repeat('\u2603', width - 3);
+ final String expected = flags == 0 ? padding + "fo*" : "fo*" + padding;
+ assertEquals(expected, FormattableUtils.append("foobar", new
Formatter(), flags, width, 3, '\u2603', "*").toString());
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = {Integer.MIN_VALUE, -1, 0, 2, 3})
+ void testNoPadding(final int width) {
+ assertEquals("foo", FormattableUtils.append("foo", new Formatter(), 0,
width, -1).toString());
+ assertEquals("foo", FormattableUtils.append("foo", new Formatter(),
FormattableFlags.LEFT_JUSTIFY, width, -1).toString());
+ }
+
@Test
void testPercentLiteral() {
assertEquals("100% done", FormattableUtils.append("100% done", new
Formatter(), 0, -1, -1).toString());