OSDN Git Service

Merge pull request #38 from dictzip/topic/miurahr/fix-dictzip-outputstream
[dictzip-java/dictzip-java.git] / northside-io / src / main / java / tokyo / northside / io / IOUtils2.java
1 /*
2  * FileUtils library.
3  *
4  * Copyright (C) 2016 Hiroshi Miura
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 package tokyo.northside.io;
20
21 import org.jetbrains.annotations.NotNull;
22
23 import java.io.InputStream;
24 import java.io.IOException;
25 import java.util.Arrays;
26
27 /**
28  * General IO stream manipulation utility.
29  * <p>
30  * This class provides static utility methods for input/output operations.
31  * <ul>
32  * <li>contentEquals - these methods compare the content of two streams
33  * </ul>
34  * <p>
35  * The methods in this class that read a stream are buffered internally.
36  * This means that there is no cause to use a <code>BufferedInputStream</code>
37  * or <code>BufferedReader</code>. The default buffer size of 4K has been shown
38  * to be efficient in tests.
39  * <p>
40  * Wherever possible, the methods in this class do <em>not</em> flush or close
41  * the stream. This is to avoid making non-portable assumptions about the
42  * streams' origin and further use. Thus the caller is still responsible for
43  * closing streams after use.
44  * <p>
45  * Created by Hiroshi Miura on 16/04/09.
46  *
47  * @author Hiroshi Miura
48  */
49 public final class IOUtils2 {
50
51    private static final int BUF_LEN = 4096;
52
53     /**
54      * Compare the contents of two Streams to determine if they are equal or not.
55      *
56      * @param first  first input stream.
57      * @param second  second input stream.
58      * @param off     compare from offset
59      * @param len     comparison length
60      * @return boolean true if content of input streams are equal, true if streams are equal,
61      *     otherwise false.
62      * @throws IOException when I/O error occurred.
63      */
64     public static boolean contentEquals(@NotNull final InputStream first, @NotNull final InputStream second,
65                                         final long off, final long len) throws IOException {
66         boolean result;
67
68         if (len < 1) {
69             throw new IllegalArgumentException();
70         }
71         if (off < 0) {
72             throw new IllegalArgumentException();
73         }
74         if (first.equals(second)) {
75             return false;
76         }
77
78         byte[] firstBytes = new byte[BUF_LEN];
79         byte[] secondBytes = new byte[BUF_LEN];
80
81         if (off > 0) {
82             long totalSkipped = 0;
83             while (totalSkipped < off) {
84                 long skipped = first.skip(off - totalSkipped);
85                 if (skipped == 0) {
86                     throw new IOException("Cannot seek offset bytes.");
87                 }
88                 totalSkipped += skipped;
89             }
90             totalSkipped = 0;
91             while (totalSkipped < off) {
92                 long skipped = second.skip(off - totalSkipped);
93                 if (skipped == 0) {
94                     throw new IOException("Cannot seek offset bytes.");
95                 }
96                 totalSkipped += skipped;
97             }
98         }
99
100         long readLengthTotal = 0;
101         result = true;
102         while (readLengthTotal < len) {
103             int readLength = BUF_LEN;
104             if (len - readLengthTotal < (long) BUF_LEN) {
105                 readLength = (int) (len - readLengthTotal);
106             }
107             int lenFirst = first.read(firstBytes, 0, readLength);
108             int lenSecond = second.read(secondBytes, 0, readLength);
109             if (lenFirst != lenSecond) {
110                 result = false;
111                 break;
112             }
113             if ((lenFirst < 0) && (lenSecond < 0)) {
114                 result = true;
115                 break;
116             }
117             readLengthTotal += lenFirst;
118             if (lenFirst < firstBytes.length) {
119                 byte[] a = Arrays.copyOfRange(firstBytes, 0, lenFirst);
120                 byte[] b = Arrays.copyOfRange(secondBytes, 0, lenSecond);
121                 if (!Arrays.equals(a, b)) {
122                     result = false;
123                     break;
124                 }
125             } else if (!Arrays.equals(firstBytes, secondBytes)) {
126                 result = false;
127                 break;
128             }
129         }
130         return result;
131     }
132
133     /**
134      * Static utility should not be instantiated.
135      */
136     private IOUtils2() {
137     }
138 }