diff --git a/src/main/java/org/apache/commons/io/IOUtils.java b/src/main/java/org/apache/commons/io/IOUtils.java index 27cfeac7b8a..83b7d91ddba 100644 --- a/src/main/java/org/apache/commons/io/IOUtils.java +++ b/src/main/java/org/apache/commons/io/IOUtils.java @@ -813,6 +813,12 @@ public static boolean contentEquals(final Reader input1, final Reader input2) } } + private enum LastState { + r, + normal, + newLine; + } + /** * Compares the contents of two Readers to determine if they are equal or * not, ignoring EOL characters. @@ -836,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 2d61f2a0e6b..26142b57bf5 100644 --- a/src/test/java/org/apache/commons/io/IOUtilsTestCase.java +++ b/src/test/java/org/apache/commons/io/IOUtilsTestCase.java @@ -314,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 {