From 612595106054d0ff2320cc15f88cfef8959e1531 Mon Sep 17 00:00:00 2001 From: Benedikt Ritter Date: Sat, 26 Apr 2014 10:39:03 +0000 Subject: [PATCH] LANG-993: Add zero copy write method to StrBuilder; LANG-994: Add zero copy read method to StrBuilder. Thanks to Mikhail Mazursky. git-svn-id: https://svn.apache.org/repos/asf/commons/proper/lang/trunk@1590224 13f79535-47bb-0310-9956-ffa450edef68 --- src/changes/changes.xml | 2 + .../apache/commons/lang3/text/StrBuilder.java | 70 ++++++++++ .../commons/lang3/text/StrBuilderTest.java | 127 ++++++++++++++++++ 3 files changed, 199 insertions(+) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 8c5148466..f2594a0de 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -22,6 +22,8 @@ + Add zero copy read method to StrBuilder + Add zero copy write method to StrBuilder Javadoc is not clear on preferred pattern to instantiate FastDateParser / FastDatePrinter FastDateParser should be case insensitive Fix bug with stripping spaces on last line in WordUtils.wrap() diff --git a/src/main/java/org/apache/commons/lang3/text/StrBuilder.java b/src/main/java/org/apache/commons/lang3/text/StrBuilder.java index f0f3742c2..23e40d6fb 100644 --- a/src/main/java/org/apache/commons/lang3/text/StrBuilder.java +++ b/src/main/java/org/apache/commons/lang3/text/StrBuilder.java @@ -16,9 +16,11 @@ */ package org.apache.commons.lang3.text; +import java.io.IOException; import java.io.Reader; import java.io.Serializable; import java.io.Writer; +import java.nio.CharBuffer; import java.util.Iterator; import java.util.List; @@ -421,6 +423,48 @@ public class StrBuilder implements CharSequence, Appendable, Serializable, Build System.arraycopy(buffer, startIndex, destination, destinationIndex, endIndex - startIndex); } + //----------------------------------------------------------------------- + /** + * If possible, reads chars from the provided {@link Readable} directly into underlying + * character buffer without making extra copies. + * + * @param readable object to read from + * @return the number of characters read + * @throws IOException if an I/O error occurs + * + * @since 3.4 + * @see #appendTo(Appendable) + */ + public int readFrom(final Readable readable) throws IOException { + final int oldSize = size; + if (readable instanceof Reader) { + final Reader r = (Reader) readable; + ensureCapacity(size + 1); + int read; + while ((read = r.read(buffer, size, buffer.length - size)) != -1) { + size += read; + ensureCapacity(size + 1); + } + } else if (readable instanceof CharBuffer) { + final CharBuffer cb = (CharBuffer) readable; + final int remaining = cb.remaining(); + ensureCapacity(size + remaining); + cb.get(buffer, size, remaining); + size += remaining; + } else { + while (true) { + ensureCapacity(size + 1); + final CharBuffer buf = CharBuffer.wrap(buffer, size, buffer.length - size); + final int read = readable.read(buf); + if (read == -1) { + break; + } + size += read; + } + } + return size - oldSize; + } + //----------------------------------------------------------------------- /** * Appends the new line string to this string builder. @@ -2610,6 +2654,32 @@ public class StrBuilder implements CharSequence, Appendable, Serializable, Build return new StrBuilderWriter(); } + /** + * Appends current contents of this StrBuilder to the + * provided {@link Appendable}. + *

+ * This method tries to avoid doing any extra copies of contents. + * + * @param appendable the appendable to append data to + * @throws IOException if an I/O error occurs + * + * @since 3.4 + * @see #readFrom(Readable) + */ + public void appendTo(final Appendable appendable) throws IOException { + if (appendable instanceof Writer) { + ((Writer) appendable).write(buffer, 0, size); + } else if (appendable instanceof StringBuilder) { + ((StringBuilder) appendable).append(buffer, 0, size); + } else if (appendable instanceof StringBuffer) { + ((StringBuffer) appendable).append(buffer, 0, size); + } else if (appendable instanceof CharBuffer) { + ((CharBuffer) appendable).put(buffer, 0, size); + } else { + appendable.append(this); + } + } + //----------------------------------------------------------------------- // /** // * Gets a String version of the string builder by calling the internal diff --git a/src/test/java/org/apache/commons/lang3/text/StrBuilderTest.java b/src/test/java/org/apache/commons/lang3/text/StrBuilderTest.java index c5fdfdc11..51699c11b 100644 --- a/src/test/java/org/apache/commons/lang3/text/StrBuilderTest.java +++ b/src/test/java/org/apache/commons/lang3/text/StrBuilderTest.java @@ -19,8 +19,13 @@ package org.apache.commons.lang3.text; import org.junit.Test; import static org.junit.Assert.*; + +import java.io.IOException; import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; import java.io.Writer; +import java.nio.CharBuffer; import java.util.Arrays; import org.apache.commons.lang3.ArrayUtils; @@ -91,6 +96,84 @@ public class StrBuilderTest { assertSame(sb, sb.trim()); } + //----------------------------------------------------------------------- + @Test + public void testReadFromReader() throws Exception { + String s = ""; + for (int i = 0; i < 100; ++i) { + StrBuilder sb = new StrBuilder(); + int len = sb.readFrom(new StringReader(s)); + + assertEquals(s.length(), len); + assertEquals(s, sb.toString()); + + s += Integer.toString(i); + } + } + + @Test + public void testReadFromReaderAppendsToEnd() throws Exception { + StrBuilder sb = new StrBuilder("Test"); + sb.readFrom(new StringReader(" 123")); + assertEquals("Test 123", sb.toString()); + } + + @Test + public void testReadFromCharBuffer() throws Exception { + String s = ""; + for (int i = 0; i < 100; ++i) { + StrBuilder sb = new StrBuilder(); + int len = sb.readFrom(CharBuffer.wrap(s)); + + assertEquals(s.length(), len); + assertEquals(s, sb.toString()); + + s += Integer.toString(i); + } + } + + @Test + public void testReadFromCharBufferAppendsToEnd() throws Exception { + StrBuilder sb = new StrBuilder("Test"); + sb.readFrom(CharBuffer.wrap(" 123")); + assertEquals("Test 123", sb.toString()); + } + + @Test + public void testReadFromReadable() throws Exception { + String s = ""; + for (int i = 0; i < 100; ++i) { + StrBuilder sb = new StrBuilder(); + int len = sb.readFrom(new MockReadable(s)); + + assertEquals(s.length(), len); + assertEquals(s, sb.toString()); + + s += Integer.toString(i); + } + } + + @Test + public void testReadFromReadableAppendsToEnd() throws Exception { + StrBuilder sb = new StrBuilder("Test"); + sb.readFrom(new MockReadable(" 123")); + assertEquals("Test 123", sb.toString()); + } + + private static class MockReadable implements Readable { + + private final CharBuffer src; + + public MockReadable(final String src) { + this.src = CharBuffer.wrap(src); + } + + @Override + public int read(final CharBuffer cb) throws IOException { + return src.read(cb); + } + } + //----------------------------------------------------------------------- @Test public void testGetSetNewLineText() { @@ -1855,4 +1938,48 @@ public class StrBuilderTest { assertEquals(sb.toString(), sb.build()); } + //----------------------------------------------------------------------- + @Test + public void testAppendToWriter() throws Exception { + final StrBuilder sb = new StrBuilder("1234567890"); + final StringWriter writer = new StringWriter(); + writer.append("Test "); + + sb.appendTo(writer); + + assertEquals("Test 1234567890", writer.toString()); + } + + @Test + public void testAppendToStringBuilder() throws Exception { + final StrBuilder sb = new StrBuilder("1234567890"); + final StringBuilder builder = new StringBuilder("Test "); + + sb.appendTo(builder); + + assertEquals("Test 1234567890", builder.toString()); + } + + @Test + public void testAppendToStringBuffer() throws Exception { + final StrBuilder sb = new StrBuilder("1234567890"); + final StringBuffer buffer = new StringBuffer("Test "); + + sb.appendTo(buffer); + + assertEquals("Test 1234567890", buffer.toString()); + } + + @Test + public void testAppendToCharBuffer() throws Exception { + final StrBuilder sb = new StrBuilder("1234567890"); + final String text = "Test "; + final CharBuffer buffer = CharBuffer.allocate(sb.size() + text.length()); + buffer.put(text); + + sb.appendTo(buffer); + + buffer.flip(); + assertEquals("Test 1234567890", buffer.toString()); + } }