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());

Reply via email to