1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package org.apache.commons.io.channels; 19 20 import java.io.IOException; 21 import java.nio.ByteBuffer; 22 import java.nio.channels.FileChannel; 23 import java.nio.channels.ReadableByteChannel; 24 import java.nio.channels.SeekableByteChannel; 25 import java.util.Objects; 26 27 import org.apache.commons.io.IOUtils; 28 29 /** 30 * Works with {@link FileChannel}. 31 * 32 * @since 2.15.0 33 */ 34 public final class FileChannels { 35 36 /** 37 * Tests if two file channel contents are equal starting at their respective current positions. 38 * 39 * @param channel1 A file channel. 40 * @param channel2 Another file channel. 41 * @param bufferCapacity The two internal buffer capacities, in bytes. 42 * @return true if the contents of both RandomAccessFiles are equal, false otherwise. 43 * @throws IOException if an I/O error occurs. 44 * @deprecated Use {@link #contentEquals(SeekableByteChannel, SeekableByteChannel, int)}. 45 */ 46 @Deprecated 47 public static boolean contentEquals(final FileChannel channel1, final FileChannel channel2, final int bufferCapacity) throws IOException { 48 return contentEquals((SeekableByteChannel) channel1, channel2, bufferCapacity); 49 } 50 51 /** 52 * Tests if two readable byte channel contents are equal starting at their respective current positions. 53 * 54 * @param channel1 A readable byte channel. 55 * @param channel2 Another readable byte channel. 56 * @param bufferCapacity The two internal buffer capacities, in bytes. 57 * @return true if the contents of both RandomAccessFiles are equal, false otherwise. 58 * @throws IOException if an I/O error occurs or the timeout is met. 59 * @since 2.19.0 60 */ 61 public static boolean contentEquals(final ReadableByteChannel channel1, final ReadableByteChannel channel2, final int bufferCapacity) throws IOException { 62 // Before making any changes, please test with org.apache.commons.io.jmh.IOUtilsContentEqualsInputStreamsBenchmark 63 // Short-circuit test 64 if (Objects.equals(channel1, channel2)) { 65 return true; 66 } 67 // Don't use ByteBuffer#compact() to avoid extra copying. 68 final ByteBuffer c1Buffer = ByteBuffer.allocateDirect(bufferCapacity); 69 final ByteBuffer c2Buffer = ByteBuffer.allocateDirect(bufferCapacity); 70 int c1NumRead = 0; 71 int c2NumRead = 0; 72 boolean c1Read0 = false; 73 boolean c2Read0 = false; 74 // If a channel is a non-blocking channel, it may return 0 bytes read for any given call. 75 while (true) { 76 if (!c2Read0) { 77 c1NumRead = readToLimit(channel1, c1Buffer); 78 c1Buffer.clear(); 79 c1Read0 = c1NumRead == 0; 80 } 81 if (!c1Read0) { 82 c2NumRead = readToLimit(channel2, c2Buffer); 83 c2Buffer.clear(); 84 c2Read0 = c2NumRead == 0; 85 } 86 if (c1NumRead == IOUtils.EOF && c2NumRead == IOUtils.EOF) { 87 return c1Buffer.equals(c2Buffer); 88 } 89 if (c1NumRead == 0 || c2NumRead == 0) { 90 // 0 may be returned from a non-blocking channel. 91 Thread.yield(); 92 continue; 93 } 94 if (c1NumRead != c2NumRead) { 95 return false; 96 } 97 if (!c1Buffer.equals(c2Buffer)) { 98 return false; 99 } 100 } 101 } 102 103 /** 104 * Tests if two seekable byte channel contents are equal starting at their respective current positions. 105 * <p> 106 * If the two channels have different sizes, no content comparison takes place, and this method returns false. 107 * </p> 108 * 109 * @param channel1 A seekable byte channel. 110 * @param channel2 Another seekable byte channel. 111 * @param bufferCapacity The two internal buffer capacities, in bytes. 112 * @return true if the contents of both RandomAccessFiles are equal, false otherwise. 113 * @throws IOException if an I/O error occurs or the timeout is met. 114 * @since 2.19.0 115 */ 116 public static boolean contentEquals(final SeekableByteChannel channel1, final SeekableByteChannel channel2, final int bufferCapacity) throws IOException { 117 // Short-circuit test 118 if (Objects.equals(channel1, channel2)) { 119 return true; 120 } 121 // Short-circuit test 122 final long size1 = size(channel1); 123 final long size2 = size(channel2); 124 if (size1 != size2) { 125 return false; 126 } 127 return size1 == 0 && size2 == 0 || contentEquals((ReadableByteChannel) channel1, channel2, bufferCapacity); 128 } 129 130 /** 131 * Reads a sequence of bytes from a channel into the given buffer until the buffer reaches its limit or the channel has reaches end-of-stream. 132 * <p> 133 * The buffer's limit is not changed. 134 * </p> 135 * 136 * @param channel The source channel. 137 * @param dst The buffer into which bytes are to be transferred. 138 * @return The number of bytes read, <em>never</em> zero, or {@code -1} if the channel has reached end-of-stream 139 * @throws IOException If some other I/O error occurs. 140 * @throws IllegalArgumentException If there is room in the given buffer. 141 */ 142 private static int readToLimit(final ReadableByteChannel channel, final ByteBuffer dst) throws IOException { 143 if (!dst.hasRemaining()) { 144 throw new IllegalArgumentException(); 145 } 146 int totalRead = 0; 147 while (dst.hasRemaining()) { 148 final int numRead; 149 if ((numRead = channel.read(dst)) == IOUtils.EOF) { 150 break; 151 } 152 if (numRead == 0) { 153 // 0 may be returned from a non-blocking channel. 154 Thread.yield(); 155 } else { 156 totalRead += numRead; 157 } 158 } 159 return totalRead != 0 ? totalRead : IOUtils.EOF; 160 } 161 162 private static long size(final SeekableByteChannel channel) throws IOException { 163 return channel != null ? channel.size() : 0; 164 } 165 166 /** 167 * Don't instantiate. 168 */ 169 private FileChannels() { 170 // no-op 171 } 172 }