1
2
3
4
5
6
7
8
9
10
11
12
13
14 package org.apache.commons.io.input;
15
16 import static org.apache.commons.io.IOUtils.EOF;
17
18 import java.io.BufferedInputStream;
19 import java.io.File;
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.nio.ByteBuffer;
23 import java.nio.channels.FileChannel;
24 import java.nio.file.Path;
25 import java.nio.file.StandardOpenOption;
26 import java.util.Objects;
27
28 import org.apache.commons.io.IOUtils;
29 import org.apache.commons.io.build.AbstractStreamBuilder;
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45 public final class BufferedFileChannelInputStream extends InputStream {
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74 public static class Builder extends AbstractStreamBuilder<BufferedFileChannelInputStream, Builder> {
75
76 private FileChannel fileChannel;
77
78
79
80
81 public Builder() {
82
83 }
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107 @Override
108 public BufferedFileChannelInputStream get() throws IOException {
109 return fileChannel != null ? new BufferedFileChannelInputStream(fileChannel, getBufferSize())
110 : new BufferedFileChannelInputStream(getPath(), getBufferSize());
111 }
112
113
114
115
116
117
118
119
120
121
122
123 public Builder setFileChannel(final FileChannel fileChannel) {
124 this.fileChannel = fileChannel;
125 return this;
126 }
127
128 }
129
130
131
132
133
134
135
136 public static Builder builder() {
137 return new Builder();
138 }
139
140 private final ByteBuffer byteBuffer;
141
142 private final FileChannel fileChannel;
143
144
145
146
147
148
149
150
151 @Deprecated
152 public BufferedFileChannelInputStream(final File file) throws IOException {
153 this(file, IOUtils.DEFAULT_BUFFER_SIZE);
154 }
155
156
157
158
159
160
161
162
163
164 @Deprecated
165 public BufferedFileChannelInputStream(final File file, final int bufferSize) throws IOException {
166 this(file.toPath(), bufferSize);
167 }
168
169 private BufferedFileChannelInputStream(final FileChannel fileChannel, final int bufferSize) {
170 this.fileChannel = Objects.requireNonNull(fileChannel, "path");
171 byteBuffer = ByteBuffer.allocateDirect(bufferSize);
172 byteBuffer.flip();
173 }
174
175
176
177
178
179
180
181
182 @Deprecated
183 public BufferedFileChannelInputStream(final Path path) throws IOException {
184 this(path, IOUtils.DEFAULT_BUFFER_SIZE);
185 }
186
187
188
189
190
191
192
193
194
195 @SuppressWarnings("resource")
196 @Deprecated
197 public BufferedFileChannelInputStream(final Path path, final int bufferSize) throws IOException {
198 this(FileChannel.open(path, StandardOpenOption.READ), bufferSize);
199 }
200
201 @Override
202 public synchronized int available() throws IOException {
203 if (!fileChannel.isOpen()) {
204 return 0;
205 }
206 if (!refill()) {
207 return 0;
208 }
209 return byteBuffer.remaining();
210 }
211
212
213
214
215
216
217
218
219
220 private void clean(final ByteBuffer buffer) {
221 if (buffer.isDirect()) {
222 cleanDirectBuffer(buffer);
223 }
224 }
225
226
227
228
229
230
231
232
233
234 private void cleanDirectBuffer(final ByteBuffer buffer) {
235 if (ByteBufferCleaner.isSupported()) {
236 ByteBufferCleaner.clean(buffer);
237 }
238 }
239
240 @Override
241 public synchronized void close() throws IOException {
242 try {
243 fileChannel.close();
244 } finally {
245 clean(byteBuffer);
246 }
247 }
248
249 @Override
250 public synchronized int read() throws IOException {
251 if (!refill()) {
252 return EOF;
253 }
254 return byteBuffer.get() & 0xFF;
255 }
256
257 @Override
258 public synchronized int read(final byte[] b, final int offset, int len) throws IOException {
259 if (offset < 0 || len < 0 || offset + len < 0 || offset + len > b.length) {
260 throw new IndexOutOfBoundsException();
261 }
262 if (!refill()) {
263 return EOF;
264 }
265 len = Math.min(len, byteBuffer.remaining());
266 byteBuffer.get(b, offset, len);
267 return len;
268 }
269
270
271
272
273
274
275
276 private boolean refill() throws IOException {
277 Input.checkOpen(fileChannel.isOpen());
278 if (!byteBuffer.hasRemaining()) {
279 byteBuffer.clear();
280 int nRead = 0;
281 while (nRead == 0) {
282 nRead = fileChannel.read(byteBuffer);
283 }
284 byteBuffer.flip();
285 return nRead >= 0;
286 }
287 return true;
288 }
289
290 @Override
291 public synchronized long skip(final long n) throws IOException {
292 if (n <= 0L) {
293 return 0L;
294 }
295 if (byteBuffer.remaining() >= n) {
296
297 byteBuffer.position(byteBuffer.position() + (int) n);
298 return n;
299 }
300 final long skippedFromBuffer = byteBuffer.remaining();
301 final long toSkipFromFileChannel = n - skippedFromBuffer;
302
303 byteBuffer.position(0);
304 byteBuffer.flip();
305 return skippedFromBuffer + skipFromFileChannel(toSkipFromFileChannel);
306 }
307
308 private long skipFromFileChannel(final long n) throws IOException {
309 final long currentFilePosition = fileChannel.position();
310 final long size = fileChannel.size();
311 if (n > size - currentFilePosition) {
312 fileChannel.position(size);
313 return size - currentFilePosition;
314 }
315 fileChannel.position(currentFilePosition + n);
316 return n;
317 }
318
319 }