OSDN Git Service

38f57739468e0d14acf9fe3f8455bd96ace5ce45
[dictzip-java/dictzip-java.git] / dictzip-lib / src / main / java / org / dict / zip / RandomAccessInputStream.java
1 /*
2  * DictZip library.
3  *
4  * Copyright (C) 2001-2004 Ho Ngoc Duc
5  * Copyright (C) 2016-2022 Hiroshi Miura
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20  */
21 package org.dict.zip;
22
23 import org.jetbrains.annotations.NotNull;
24
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.io.RandomAccessFile;
28
29 /**
30  * RandomAccessInputStream.
31  * Buffering RandomAccessFile and provide InputStream interface.
32  *
33  * @author Ho Ngoc Duc
34  * @author Hiroshi Miura
35  */
36 public class RandomAccessInputStream extends InputStream {
37     private static final int DEFAULT_BUFSIZE = 4096;
38     private final RandomAccessFile in;
39     private final byte[] inbuf;
40     private final int bufsize;
41
42     private long currentpos = 0;
43     private long startpos = -1;
44     private long endpos = -1;
45     private long mark = 0;
46
47
48     /**
49      * Constructor of RandomAccessInputStream, accept RandomAccessFile and buffer size.
50      * @param inFile RandomAccessFile file.
51      * @param bufsize buffer size.
52      */
53     public RandomAccessInputStream(final RandomAccessFile inFile, final int bufsize) {
54         this.in = inFile;
55         this.bufsize = bufsize;
56         inbuf = new byte[bufsize];
57     }
58
59     /**
60      * Construct RandomAccessInputStream from file.
61      *
62      * @param inFile RandomAccessFile
63      */
64     public RandomAccessInputStream(final RandomAccessFile inFile) {
65         this(inFile, DEFAULT_BUFSIZE);
66     }
67
68     /**
69      * Construct RandomAccessInputStream from filename.
70      *
71      * @param file to read with random access.
72      * @param mode open mode.
73      * @exception IOException if an I/O error has occurred.
74      */
75     public RandomAccessInputStream(final String file, final String mode) throws IOException {
76         this(new RandomAccessFile(file, mode));
77     }
78
79     /**
80      * {@inheritDoc}
81      */
82     @Override
83     public final int available() throws IOException {
84         long available =  length() - position();
85         if (available > Integer.MAX_VALUE) {
86             return Integer.MAX_VALUE;
87         }
88         return (int) available;
89     }
90
91     /**
92      * {@inheritDoc}
93      */
94     @Override
95     public final void close() throws IOException {
96         in.close();
97     }
98
99     /**
100      * Get file length.
101      *
102      * @return length of file in byte.
103      * @exception IOException if an I/O error has occurred.
104      */
105     public final long length() throws IOException {
106         return in.length();
107     }
108
109     public final int getLength() throws IOException {
110         return (int) length();
111     }
112
113
114     /**
115      * Get cursor position.
116      *
117      * @return position in byte.
118      * @exception IOException if an I/O error has occurred.
119      */
120     public final long position() throws IOException {
121         return currentpos;
122     }
123
124     @Deprecated
125     public final int getPos() throws IOException {
126         return (int) position();
127     }
128
129     /**
130      * {@inheritDoc}
131      */
132     @Override
133     public final synchronized void mark(final int markpos) {
134         mark = currentpos;
135     }
136
137     /**
138      * {@inheritDoc}
139      */
140     @Override
141     public final boolean markSupported() {
142         return true;
143     }
144
145     /**
146      * {@inheritDoc}
147      */
148     @Override
149     public final synchronized int read() throws IOException {
150         int c = read(currentpos);
151         if (c == -1) {
152             return -1;
153         }
154         currentpos++;
155         return c;
156     }
157
158     /**
159      * Read one byte from specified position.
160      * This method does not modify this stream's position.
161      * If the given position is greater than the file's current size then no bytes are read and return -1.
162      * @param pos position to read.
163      * @return -1 when position is greater than the file's current size, otherwise byte value.
164      */
165     public int read(long pos) {
166         if (pos < startpos || pos > endpos) {
167             long blockstart = (pos/ bufsize) * bufsize;
168             int n;
169             try {
170                 in.seek(blockstart);
171                 n = in.read(inbuf);
172             } catch (IOException e) {
173                 return -1;
174             }
175             startpos = blockstart;
176             endpos = blockstart + n - 1;
177             if (pos < startpos || pos > endpos) {
178                 return -1;
179             }
180         }
181         return inbuf[(int) (pos - startpos)] & 0xff;
182     }
183
184     /**
185      * {@inheritDoc}
186      */
187     @Override
188     public final int read(final byte @NotNull [] buf, final int off, final int len) throws IOException {
189         int idx = 0;
190         while (idx < len) {
191             int c = read(currentpos);
192             if (c == -1) {
193                 return idx;
194             } else {
195                 buf[off + idx++] = (byte) c;
196                 currentpos++;
197             }
198         }
199         return idx;
200     }
201
202     /**
203      * Read full data to byte buffer.
204      *
205      * @param buf buffer to store data.
206      * @exception IOException if an I/O error has occurred.
207      */
208     public final void readFully(final byte[] buf) throws IOException {
209         int idx = 0;
210         while (idx < buf.length) {
211             int c = read(currentpos);
212             if (c == -1) {
213                 throw new IOException();
214             }
215             buf[idx++] = (byte) c;
216             currentpos++;
217         }
218     }
219
220     /**
221      * {@inheritDoc}
222      */
223     @Override
224     public final synchronized void reset() throws IOException {
225         currentpos = mark;
226     }
227
228     /**
229      * Seek file position.
230      * <p>
231      *     when specified position is beyond of end of the file, position is set to end of file.
232      *
233      * @param pos file position in byte.
234      * @exception IOException if an I/O error has occurred.
235      */
236     public final void seek(final long pos) throws IOException {
237         if (pos < 0) {
238             throw new IOException("seek position is less than 0");
239         } else {
240             currentpos = Math.min(pos, length());
241         }
242     }
243
244     /**
245      * Skip n byte of input stream.
246      * <p>
247      *     when n is less than 0, it seek backward.
248      *
249      * @param n the number of bytes to be skipped.
250      * @return the actual number of bytes skipped.
251      * @throws IOException if the stream does not support seek, or if some other I/O error occurs.
252      */
253     @Override
254     public final long skip(final long n) throws IOException {
255         long previous = currentpos;
256         if (n < 0 && currentpos + n < 0) {
257             currentpos = 0;
258         } else if (currentpos + n > length()) {
259             currentpos = length();
260         } else {
261             currentpos += n;
262         }
263         return currentpos - previous;
264     }
265 }