From 66ffa500d25f6781919a68c73f78c720b2969235 Mon Sep 17 00:00:00 2001 From: XenoAmess Date: Tue, 2 Jun 2020 00:27:07 +0800 Subject: [PATCH] performance refine for IOUtils.contentEquals(Reader, Reader) --- pom.xml | 52 +++ .../java/org/apache/commons/io/IOUtils.java | 430 ++++++++++++++++-- .../apache/commons/io/IOUtilsTestCase.java | 51 ++- .../IOUtilsContentEqualsPerformanceTest.java | 178 ++++++++ 4 files changed, 663 insertions(+), 48 deletions(-) create mode 100644 src/test/java/org/apache/commons/io/performance/IOUtilsContentEqualsPerformanceTest.java diff --git a/pom.xml b/pom.xml index 3503eac4f64..02237af5657 100644 --- a/pom.xml +++ b/pom.xml @@ -230,6 +230,18 @@ file comparators, endian transformation classes, and much more. + + org.openjdk.jmh + jmh-core + ${jmh.version} + test + + + org.openjdk.jmh + jmh-generator-annprocess + ${jmh.version} + test + org.junit.jupiter junit-jupiter @@ -300,6 +312,7 @@ file comparators, endian transformation classes, and much more. true Gary Gregory 86fdc7e2a11262cb + 1.21 @@ -469,5 +482,44 @@ file comparators, endian transformation classes, and much more. true + + benchmark + + true + org.apache + + + + + org.codehaus.mojo + exec-maven-plugin + 1.6.0 + + + benchmark + test + + exec + + + test + java + + -classpath + + org.openjdk.jmh.Main + -rf + json + -rff + target/jmh-result.${benchmark}.json + ${benchmark} + + + + + + + + diff --git a/src/main/java/org/apache/commons/io/IOUtils.java b/src/main/java/org/apache/commons/io/IOUtils.java index 55c2cfd4615..83b7d91ddba 100644 --- a/src/main/java/org/apache/commons/io/IOUtils.java +++ b/src/main/java/org/apache/commons/io/IOUtils.java @@ -690,6 +690,8 @@ public static void closeQuietly(final Writer output) { closeQuietly((Closeable) output); } + private static final int CONTENT_EQUALS_CHAR_ARRAY_BUFFER_SIZE = 8192; + /** * Compares the contents of two Streams to determine if they are equal or * not. @@ -714,17 +716,40 @@ public static boolean contentEquals(final InputStream input1, final InputStream if (input1 == null ^ input2 == null) { return false; } - final BufferedInputStream bufferedInput1 = buffer(input1); - final BufferedInputStream bufferedInput2 = buffer(input2); - int ch = bufferedInput1.read(); - while (EOF != ch) { - final int ch2 = bufferedInput2.read(); - if (ch != ch2) { - return false; + + byte[] byteArray1 = new byte[CONTENT_EQUALS_CHAR_ARRAY_BUFFER_SIZE]; + byte[] byteArray2 = new byte[CONTENT_EQUALS_CHAR_ARRAY_BUFFER_SIZE]; + int nowPos1; + int nowPos2; + int nowRead1; + int nowRead2; + while (true) { + nowPos1 = 0; + nowPos2 = 0; + for (int nowCheck = 0; nowCheck < CONTENT_EQUALS_CHAR_ARRAY_BUFFER_SIZE; nowCheck++) { + if (nowPos1 == nowCheck) { + do { + nowRead1 = input1.read(byteArray1, nowPos1, CONTENT_EQUALS_CHAR_ARRAY_BUFFER_SIZE - nowPos1); + } while (nowRead1 == 0); + if (nowRead1 == -1) { + return nowPos2 == nowCheck && input2.read() == -1; + } + nowPos1 += nowRead1; + } + if (nowPos2 == nowCheck) { + do { + nowRead2 = input2.read(byteArray2, nowPos2, CONTENT_EQUALS_CHAR_ARRAY_BUFFER_SIZE - nowPos2); + } while (nowRead2 == 0); + if (nowRead2 == -1) { + return nowPos1 == nowCheck && input1.read() == -1; + } + nowPos2 += nowRead2; + } + if (byteArray1[nowCheck] != byteArray2[nowCheck]) { + return false; + } } - ch = bufferedInput1.read(); } - return bufferedInput2.read() == EOF; } /** @@ -752,19 +777,46 @@ public static boolean contentEquals(final Reader input1, final Reader input2) if (input1 == null ^ input2 == null) { return false; } - final BufferedReader bufferedInput1 = toBufferedReader(input1); - final BufferedReader bufferedInput2 = toBufferedReader(input2); - int ch = bufferedInput1.read(); - while (EOF != ch) { - final int ch2 = bufferedInput2.read(); - if (ch != ch2) { - return false; + char[] charArray1 = new char[CONTENT_EQUALS_CHAR_ARRAY_BUFFER_SIZE]; + char[] charArray2 = new char[CONTENT_EQUALS_CHAR_ARRAY_BUFFER_SIZE]; + int nowPos1; + int nowPos2; + int nowRead1; + int nowRead2; + while (true) { + nowPos1 = 0; + nowPos2 = 0; + for (int nowCheck = 0; nowCheck < CONTENT_EQUALS_CHAR_ARRAY_BUFFER_SIZE; nowCheck++) { + if (nowPos1 == nowCheck) { + do { + nowRead1 = input1.read(charArray1, nowPos1, CONTENT_EQUALS_CHAR_ARRAY_BUFFER_SIZE - nowPos1); + } while (nowRead1 == 0); + if (nowRead1 == -1) { + return nowPos2 == nowCheck && input2.read() == -1; + } + nowPos1 += nowRead1; + } + if (nowPos2 == nowCheck) { + do { + nowRead2 = input2.read(charArray2, nowPos2, CONTENT_EQUALS_CHAR_ARRAY_BUFFER_SIZE - nowPos2); + } while (nowRead2 == 0); + if (nowRead2 == -1) { + return nowPos1 == nowCheck && input1.read() == -1; + } + nowPos2 += nowRead2; + } + if (charArray1[nowCheck] != charArray2[nowCheck]) { + return false; + } } - ch = bufferedInput1.read(); } + } - return bufferedInput2.read() == EOF; + private enum LastState { + r, + normal, + newLine; } /** @@ -790,16 +842,342 @@ public static boolean contentEqualsIgnoreEOL(final Reader input1, final Reader i if (input1 == null ^ input2 == null) { return false; } - final BufferedReader br1 = toBufferedReader(input1); - final BufferedReader br2 = toBufferedReader(input2); - String line1 = br1.readLine(); - String line2 = br2.readLine(); - while (line1 != null && line1.equals(line2)) { - line1 = br1.readLine(); - line2 = br2.readLine(); + char[] charArray1 = new char[CONTENT_EQUALS_CHAR_ARRAY_BUFFER_SIZE]; + char[] charArray2 = new char[CONTENT_EQUALS_CHAR_ARRAY_BUFFER_SIZE]; + int nowPos1 = 0; + int nowPos2 = 0; + int nowRead1; + int nowRead2; + int nowCheck1 = 0; + int nowCheck2 = 0; + boolean readEnd1 = false; + boolean readEnd2 = false; + LastState lastState1 = LastState.newLine; + LastState lastState2 = LastState.newLine; + while (true) { + if (nowPos1 == nowCheck1) { + if (nowCheck1 == CONTENT_EQUALS_CHAR_ARRAY_BUFFER_SIZE) { + nowPos1 = nowCheck1 = 0; + } + do { + nowRead1 = input1.read(charArray1, nowPos1, CONTENT_EQUALS_CHAR_ARRAY_BUFFER_SIZE - nowPos1); + } while (nowRead1 == 0); + if (nowRead1 == -1) { + readEnd1 = true; + } else { + nowPos1 += nowRead1; + } + } + if (nowPos2 == nowCheck2) { + if (nowCheck2 == CONTENT_EQUALS_CHAR_ARRAY_BUFFER_SIZE) { + nowPos2 = nowCheck2 = 0; + } + do { + nowRead2 = input2.read(charArray2, nowPos2, CONTENT_EQUALS_CHAR_ARRAY_BUFFER_SIZE - nowPos2); + } while (nowRead2 == 0); + if (nowRead2 == -1) { + readEnd2 = true; + } else { + nowPos2 += nowRead2; + } + } + if (readEnd1) { + if (readEnd2) { + return true; + } else { + switch (lastState1) { + case r: + case newLine: + switch (lastState2) { + case r: + if (charArray2[nowCheck2] == '\n') { + nowCheck2++; + if (nowPos2 == nowCheck2) { + if (nowCheck2 == CONTENT_EQUALS_CHAR_ARRAY_BUFFER_SIZE) { + nowPos2 = nowCheck2 = 0; + } + do { + nowRead2 = input2.read(charArray2, nowPos2, + CONTENT_EQUALS_CHAR_ARRAY_BUFFER_SIZE - nowPos2); + } while (nowRead2 == 0); + if (nowRead2 == -1) { + readEnd2 = true; + } else { + nowPos2 += nowRead2; + } + } + return readEnd2; + } + return false; + default: + return false; + } + case normal: + switch (lastState2) { + case normal: + switch (charArray2[nowCheck2]) { + case '\r': + nowCheck2++; + if (nowPos2 == nowCheck2) { + if (nowCheck2 == CONTENT_EQUALS_CHAR_ARRAY_BUFFER_SIZE) { + nowPos2 = nowCheck2 = 0; + } + do { + nowRead2 = input2.read(charArray2, nowPos2, + CONTENT_EQUALS_CHAR_ARRAY_BUFFER_SIZE - nowPos2); + } while (nowRead2 == 0); + if (nowRead2 == -1) { + readEnd2 = true; + } else { + nowPos2 += nowRead2; + } + } + if (readEnd2) { + return true; + } else if (charArray2[nowCheck2] == '\n') { + nowCheck2++; + if (nowPos2 == nowCheck2) { + if (nowCheck2 == CONTENT_EQUALS_CHAR_ARRAY_BUFFER_SIZE) { + nowPos2 = nowCheck2 = 0; + } + do { + nowRead2 = input2.read(charArray2, nowPos2, + CONTENT_EQUALS_CHAR_ARRAY_BUFFER_SIZE - nowPos2); + } while (nowRead2 == 0); + if (nowRead2 == -1) { + readEnd2 = true; + } else { + nowPos2 += nowRead2; + } + } + return readEnd2; + } + return false; + case '\n': + nowCheck2++; + if (nowPos2 == nowCheck2) { + if (nowCheck2 == CONTENT_EQUALS_CHAR_ARRAY_BUFFER_SIZE) { + nowPos2 = nowCheck2 = 0; + } + do { + nowRead2 = input2.read(charArray2, nowPos2, + CONTENT_EQUALS_CHAR_ARRAY_BUFFER_SIZE - nowPos2); + } while (nowRead2 == 0); + if (nowRead2 == -1) { + readEnd2 = true; + } else { + nowPos2 += nowRead2; + } + } + return readEnd2; + default: + return false; + } + default: + return false; + } + default: + //shall never enter + } + } + } else if (readEnd2) { + switch (lastState2) { + case r: + case newLine: + switch (lastState1) { + case r: + if (charArray1[nowCheck1] == '\n') { + nowCheck1++; + if (nowPos1 == nowCheck1) { + if (nowCheck1 == CONTENT_EQUALS_CHAR_ARRAY_BUFFER_SIZE) { + nowPos1 = nowCheck1 = 0; + } + do { + nowRead1 = input1.read(charArray1, nowPos1, + CONTENT_EQUALS_CHAR_ARRAY_BUFFER_SIZE - nowPos1); + } while (nowRead1 == 0); + if (nowRead1 == -1) { + readEnd1 = true; + } else { + nowPos1 += nowRead1; + } + } + return readEnd1; + } + return false; + default: + return false; + } + case normal: + switch (lastState1) { + case normal: + switch (charArray1[nowCheck1]) { + case '\r': + nowCheck1++; + if (nowPos1 == nowCheck1) { + if (nowCheck1 == CONTENT_EQUALS_CHAR_ARRAY_BUFFER_SIZE) { + nowPos1 = nowCheck1 = 0; + } + do { + nowRead1 = input1.read(charArray1, nowPos1, + CONTENT_EQUALS_CHAR_ARRAY_BUFFER_SIZE - nowPos1); + } while (nowRead1 == 0); + if (nowRead1 == -1) { + readEnd1 = true; + } else { + nowPos1 += nowRead1; + } + } + if (readEnd1) { + return true; + } else if (charArray1[nowCheck1] == '\n') { + nowCheck1++; + if (nowPos1 == nowCheck1) { + if (nowCheck1 == CONTENT_EQUALS_CHAR_ARRAY_BUFFER_SIZE) { + nowPos1 = nowCheck1 = 0; + } + do { + nowRead1 = input1.read(charArray1, nowPos1, + CONTENT_EQUALS_CHAR_ARRAY_BUFFER_SIZE - nowPos1); + } while (nowRead1 == 0); + if (nowRead1 == -1) { + readEnd1 = true; + } else { + nowPos1 += nowRead1; + } + } + return readEnd1; + } + return false; + case '\n': + nowCheck1++; + if (nowPos1 == nowCheck1) { + if (nowCheck1 == CONTENT_EQUALS_CHAR_ARRAY_BUFFER_SIZE) { + nowPos1 = nowCheck1 = 0; + } + do { + nowRead1 = input1.read(charArray1, nowPos1, + CONTENT_EQUALS_CHAR_ARRAY_BUFFER_SIZE - nowPos1); + } while (nowRead1 == 0); + if (nowRead1 == -1) { + readEnd1 = true; + } else { + nowPos1 += nowRead1; + } + } + return readEnd1; + default: + return false; + } + default: + return false; + } + default: + //shall never enter + } + } + + switch (charArray1[nowCheck1]) { + case '\r': + switch (charArray2[nowCheck2]) { + case '\r': + lastState1 = lastState2 = LastState.r; + nowCheck1++; + nowCheck2++; + continue; + case '\n': + nowCheck1++; + if (nowPos1 == nowCheck1) { + if (nowCheck1 == CONTENT_EQUALS_CHAR_ARRAY_BUFFER_SIZE) { + nowPos1 = nowCheck1 = 0; + } + do { + nowRead1 = input1.read(charArray1, nowPos1, +CONTENT_EQUALS_CHAR_ARRAY_BUFFER_SIZE - nowPos1); + } while (nowRead1 == 0); + if (nowRead1 == -1) { + readEnd1 = true; + } else { + nowPos1 += nowRead1; + } + } + lastState1 = lastState2 = LastState.newLine; + nowCheck2++; + if (readEnd1) { + continue; + } + if (charArray1[nowCheck1] == '\n') { + nowCheck1++; + } + continue; + default: + return false; + } + case '\n': + switch (charArray2[nowCheck2]) { + case '\n': + lastState1 = lastState2 = LastState.newLine; + nowCheck1++; + nowCheck2++; + continue; + case '\r': + nowCheck2++; + if (nowPos2 == nowCheck2) { + if (nowCheck2 == CONTENT_EQUALS_CHAR_ARRAY_BUFFER_SIZE) { + nowPos2 = nowCheck2 = 0; + } + do { + nowRead2 = input2.read(charArray2, nowPos2, + CONTENT_EQUALS_CHAR_ARRAY_BUFFER_SIZE - nowPos2); + } while (nowRead2 == 0); + if (nowRead2 == -1) { + readEnd2 = true; + } else { + nowPos2 += nowRead2; + } + } + lastState1 = lastState2 = LastState.newLine; + nowCheck1++; + if (readEnd2) { + continue; + } + if (charArray2[nowCheck2] == '\n') { + nowCheck2++; + } + continue; + default: + if (lastState1 == LastState.r) { + lastState1 = LastState.newLine; + nowCheck1++; + continue; + } else { + return false; + } + } + default: + switch (charArray2[nowCheck2]) { + case '\n': + if (lastState2 == LastState.r) { + lastState2 = LastState.newLine; + nowCheck2++; + continue; + } else { + return false; + } + case '\r': + return false; + default: + if (charArray1[nowCheck1] != charArray2[nowCheck2]) { + return false; + } + lastState1 = lastState2 = LastState.normal; + nowCheck1++; + nowCheck2++; + continue; + } + } } - return Objects.equals(line1, line2); } /** diff --git a/src/test/java/org/apache/commons/io/IOUtilsTestCase.java b/src/test/java/org/apache/commons/io/IOUtilsTestCase.java index 4a502468e4c..26142b57bf5 100644 --- a/src/test/java/org/apache/commons/io/IOUtilsTestCase.java +++ b/src/test/java/org/apache/commons/io/IOUtilsTestCase.java @@ -259,6 +259,8 @@ public synchronized void close() throws IOException { new ByteArrayInputStream("ABC".getBytes(StandardCharsets.UTF_8)))); assertFalse(IOUtils.contentEquals(new ByteArrayInputStream("ABC".getBytes(StandardCharsets.UTF_8)), new ByteArrayInputStream("ABCD".getBytes(StandardCharsets.UTF_8)))); + assertFalse(IOUtils.contentEquals(new ByteArrayInputStream("apache".getBytes(StandardCharsets.UTF_8)), + new ByteArrayInputStream("apacha".getBytes(StandardCharsets.UTF_8)))); } @Test public void testContentEquals_Reader_Reader() throws Exception { @@ -287,6 +289,7 @@ public synchronized void close() throws IOException { assertTrue(IOUtils.contentEquals(new StringReader("ABC"), new StringReader("ABC"))); assertFalse(IOUtils.contentEquals(new StringReader("ABCD"), new StringReader("ABC"))); assertFalse(IOUtils.contentEquals(new StringReader("ABC"), new StringReader("ABCD"))); + assertFalse(IOUtils.contentEquals(new StringReader("apache"), new StringReader("apacha"))); } @Test public void testContentEqualsIgnoreEOL() throws Exception { @@ -311,28 +314,32 @@ public synchronized void close() throws IOException { assertTrue(IOUtils.contentEqualsIgnoreEOL(input1, input1)); } - Reader r1; - Reader r2; - - r1 = new CharArrayReader("".toCharArray()); - r2 = new CharArrayReader("".toCharArray()); - assertTrue(IOUtils.contentEqualsIgnoreEOL(r1, r2)); - - r1 = new CharArrayReader("1".toCharArray()); - r2 = new CharArrayReader("1".toCharArray()); - assertTrue(IOUtils.contentEqualsIgnoreEOL(r1, r2)); - - r1 = new CharArrayReader("1".toCharArray()); - r2 = new CharArrayReader("2".toCharArray()); - assertFalse(IOUtils.contentEqualsIgnoreEOL(r1, r2)); - - r1 = new CharArrayReader("123\rabc".toCharArray()); - r2 = new CharArrayReader("123\nabc".toCharArray()); - assertTrue(IOUtils.contentEqualsIgnoreEOL(r1, r2)); - - r1 = new CharArrayReader("321".toCharArray()); - r2 = new CharArrayReader("321\r\n".toCharArray()); - assertTrue(IOUtils.contentEqualsIgnoreEOL(r1, r2)); + testSingleEOL("", "", true); + testSingleEOL("1", "1", true); + testSingleEOL("1", "2", false); + testSingleEOL("123\rabc", "123\nabc", true); + testSingleEOL("321", "321\r\n", true); + testSingleEOL("321", "321\r\naabb", false); + testSingleEOL("321", "321\n", true); + testSingleEOL("321", "321\r", true); + testSingleEOL("321", "321\r\n", true); + testSingleEOL("321", "321\r\r", false); + testSingleEOL("321", "321\n\r", false); + testSingleEOL("321\n", "321", true); + testSingleEOL("321\n", "321\n\r", false); + testSingleEOL("321\n", "321\r\n", true); + testSingleEOL("321\r", "321\r\n", true); + testSingleEOL("321\r\n", "321\r\n\r", false); + } + + public void testSingleEOL(String s1, String s2, boolean ifEquals) throws IOException { + assertEquals(ifEquals, IOUtils.contentEqualsIgnoreEOL(new CharArrayReader(s1.toCharArray()), + new CharArrayReader(s2.toCharArray())), "failed at :{" + s1 + "," + s2 + "}"); + assertEquals(ifEquals, IOUtils.contentEqualsIgnoreEOL(new CharArrayReader(s2.toCharArray()), + new CharArrayReader(s1.toCharArray())), "failed at :{" + s2 + "," + s1 + "}"); + assertTrue(IOUtils.contentEqualsIgnoreEOL(new CharArrayReader(s1.toCharArray()), + new CharArrayReader(s1.toCharArray())), "failed at :{" + s1 + "," + s1 + "}"); + assertTrue(IOUtils.contentEqualsIgnoreEOL(new CharArrayReader(s2.toCharArray()), new CharArrayReader(s2.toCharArray())), "failed at :{" + s2 + "," + s2 + "}"); } @Test public void testCopy_ByteArray_OutputStream() throws Exception { diff --git a/src/test/java/org/apache/commons/io/performance/IOUtilsContentEqualsPerformanceTest.java b/src/test/java/org/apache/commons/io/performance/IOUtilsContentEqualsPerformanceTest.java new file mode 100644 index 00000000000..8fb0d19663a --- /dev/null +++ b/src/test/java/org/apache/commons/io/performance/IOUtilsContentEqualsPerformanceTest.java @@ -0,0 +1,178 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.io.performance; + +import org.apache.commons.io.IOUtils; +import org.openjdk.jmh.annotations.*; + +import java.io.*; +import java.util.concurrent.TimeUnit; + +import static org.apache.commons.io.IOUtils.EOF; +import static org.apache.commons.io.IOUtils.toBufferedReader; + +/** + * Test to show whether using BitSet for removeAll() methods is faster than using HashSet. + */ +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +public class IOUtilsContentEqualsPerformanceTest { + + private static final String[] STRINGS = new String[3]; + + static { + STRINGS[0] = getString0(); + STRINGS[1] = STRINGS[0] + 'c'; + STRINGS[2] = STRINGS[0] + 'd'; + } + + private static final int LOOP = 10; + + @Benchmark + public boolean[] testContentEqualsForFileNew() throws IOException { + boolean[] res = new boolean[2]; + for (int i = 0; i < LOOP; i++) { + try (InputStream inputStream1 = + this.getClass().getResourceAsStream("/org/apache/commons/io/testfileBOM.xml"); + InputStream inputStream2 = + this.getClass().getResourceAsStream("/org/apache/commons/io/testfileNoBOM.xml"); + Reader inputReader1 = new InputStreamReader(inputStream1); + Reader inputReader2 = new InputStreamReader(inputStream2); + ) { + res[0] = IOUtils.contentEquals(inputReader1, inputReader2); + } + try (InputStream inputStream1 = + this.getClass().getResourceAsStream("/org/apache/commons/io/testfileBOM.xml"); + InputStream inputStream2 = + this.getClass().getResourceAsStream("/org/apache/commons/io/testfileBOM.xml"); + Reader inputReader1 = new InputStreamReader(inputStream1); + Reader inputReader2 = new InputStreamReader(inputStream2); + ) { + res[1] = IOUtils.contentEquals(inputReader1, inputReader2); + } + } + return res; + } + + @Benchmark + public boolean[] testContentEqualsOld() throws IOException { + boolean[] res = new boolean[2]; + for (int i = 0; i < LOOP; i++) { + try (InputStream inputStream1 = + this.getClass().getResourceAsStream("/org/apache/commons/io/testfileBOM.xml"); + InputStream inputStream2 = + this.getClass().getResourceAsStream("/org/apache/commons/io/testfileNoBOM.xml"); + Reader inputReader1 = new InputStreamReader(inputStream1); + Reader inputReader2 = new InputStreamReader(inputStream2); + ) { + res[0] = contentEqualsOld(inputReader1, inputReader2); + } + try (InputStream inputStream1 = + this.getClass().getResourceAsStream("/org/apache/commons/io/testfileBOM.xml"); + InputStream inputStream2 = + this.getClass().getResourceAsStream("/org/apache/commons/io/testfileBOM.xml"); + Reader inputReader1 = new InputStreamReader(inputStream1); + Reader inputReader2 = new InputStreamReader(inputStream2); + ) { + res[1] = contentEqualsOld(inputReader1, inputReader2); + } + } + return res; + } + + @Benchmark + public boolean[] testContentEqualsNew2() throws IOException { + boolean[] res = new boolean[9]; + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + try (Reader inputReader1 = new StringReader(STRINGS[i]); + Reader inputReader2 = new StringReader(STRINGS[j]); + ) { + res[i * 3 + j] = IOUtils.contentEquals(inputReader1, inputReader2); + } + } + } + return res; + } + + @Benchmark + public boolean[] testContentEqualsOld2() throws IOException { + boolean[] res = new boolean[9]; + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + try (Reader inputReader1 = new StringReader(STRINGS[i]); + Reader inputReader2 = new StringReader(STRINGS[j]); + ) { + res[i * 3 + j] = contentEqualsOld(inputReader1, inputReader2); + } + } + } + return res; + } + + /** + * Old version of IOUtils.contentEquals(Reader, Reader) + * + * Compares the contents of two Readers to determine if they are equal or + * not. + *

+ * This method buffers the input internally using + * BufferedReader if they are not already buffered. + *

+ * + * @param input1 the first reader + * @param input2 the second reader + * @return true if the content of the readers are equal or they both don't + * exist, false otherwise + * @throws NullPointerException if either input is null + * @throws IOException if an I/O error occurs + * @since 1.1 + */ + @SuppressWarnings("resource") + public static boolean contentEqualsOld(final Reader input1, final Reader input2) + throws IOException { + if (input1 == input2) { + return true; + } + if (input1 == null ^ input2 == null) { + return false; + } + final BufferedReader bufferedInput1 = toBufferedReader(input1); + final BufferedReader bufferedInput2 = toBufferedReader(input2); + + int ch = bufferedInput1.read(); + while (EOF != ch) { + final int ch2 = bufferedInput2.read(); + if (ch != ch2) { + return false; + } + ch = bufferedInput1.read(); + } + + return bufferedInput2.read() == EOF; + } + + public static String getString0() { + StringBuilder stringBuilder = new StringBuilder("ab"); + for (int i = 0; i < 24; i++) { + stringBuilder.append(stringBuilder); + } + return stringBuilder.toString(); + } + +} \ No newline at end of file