1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.imaging.bytesource;
18
19 import java.io.BufferedInputStream;
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.util.Arrays;
23 import java.util.Objects;
24
25 import org.apache.commons.imaging.ImagingException;
26 import org.apache.commons.imaging.common.Allocator;
27 import org.apache.commons.imaging.common.BinaryFunctions;
28 import org.apache.commons.io.IOUtils;
29 import org.apache.commons.io.build.AbstractOrigin.InputStreamOrigin;
30
31 final class InputStreamByteSource extends ByteSource {
32
33 private final class Block {
34
35 public final byte[] bytes;
36 private Block next;
37 private boolean triedNext;
38
39 Block(final byte[] bytes) {
40 this.bytes = bytes;
41 }
42
43 public Block getNext() throws IOException {
44 if (null != next) {
45 return next;
46 }
47 if (triedNext) {
48 return null;
49 }
50 triedNext = true;
51 next = readBlock();
52 return next;
53 }
54
55 }
56
57 private final class BlockInputStream extends InputStream {
58 private Block block;
59 private boolean readFirst;
60 private int blockIndex;
61
62 @Override
63 public int read() throws IOException {
64 if (null == block) {
65 if (readFirst) {
66 return -1;
67 }
68 block = getFirstBlock();
69 readFirst = true;
70 }
71
72 if (block != null && blockIndex >= block.bytes.length) {
73 block = block.getNext();
74 blockIndex = 0;
75 }
76
77 if (null == block) {
78 return -1;
79 }
80
81 if (blockIndex >= block.bytes.length) {
82 return -1;
83 }
84
85 return 0xff & block.bytes[blockIndex++];
86 }
87
88 @Override
89 public int read(final byte[] array, final int off, final int len) throws IOException {
90
91 Objects.requireNonNull(array, "array");
92 if (off < 0 || off > array.length || len < 0 || off + len > array.length || off + len < 0) {
93 throw new IndexOutOfBoundsException();
94 }
95 if (len == 0) {
96 return 0;
97 }
98
99
100
101 if (null == block) {
102 if (readFirst) {
103 return -1;
104 }
105 block = getFirstBlock();
106 readFirst = true;
107 }
108
109 if (block != null && blockIndex >= block.bytes.length) {
110 block = block.getNext();
111 blockIndex = 0;
112 }
113
114 if (null == block) {
115 return -1;
116 }
117
118 if (blockIndex >= block.bytes.length) {
119 return -1;
120 }
121
122 final int readSize = Math.min(len, block.bytes.length - blockIndex);
123 System.arraycopy(block.bytes, blockIndex, array, off, readSize);
124 blockIndex += readSize;
125 return readSize;
126 }
127
128 @Override
129 public long skip(final long n) throws IOException {
130
131 long remaining = n;
132
133 if (n <= 0) {
134 return 0;
135 }
136
137 while (remaining > 0) {
138
139 if (null == block) {
140 if (readFirst) {
141 return -1;
142 }
143 block = getFirstBlock();
144 readFirst = true;
145 }
146
147
148 if (block != null && blockIndex >= block.bytes.length) {
149 block = block.getNext();
150 blockIndex = 0;
151 }
152
153 if (null == block) {
154 break;
155 }
156
157 if (blockIndex >= block.bytes.length) {
158 break;
159 }
160
161 final int readSize = Math.min((int) Math.min(BLOCK_SIZE, remaining), block.bytes.length - blockIndex);
162
163 blockIndex += readSize;
164 remaining -= readSize;
165 }
166
167 return n - remaining;
168 }
169
170 }
171
172 private static final int BLOCK_SIZE = 1024;
173 private final InputStream inputStream;
174 private Block headBlock;
175 private byte[] readBuffer;
176 private long streamLength = -1;
177
178 InputStreamByteSource(final InputStream inputStream, final String fileName) {
179 super(new InputStreamOrigin(inputStream), fileName);
180 this.inputStream = new BufferedInputStream(inputStream);
181 }
182
183 @Override
184 public byte[] getByteArray(final long position, final int length) throws IOException {
185
186 if (position < 0 || length < 0 || position + length < 0 || position + length > size()) {
187 throw new ImagingException(
188 "Could not read block (block start: " + position + ", block length: " + length + ", data length: " + streamLength + ").");
189 }
190
191 final InputStream cis = getInputStream();
192 BinaryFunctions.skipBytes(cis, position);
193
194 final byte[] bytes = Allocator.byteArray(length);
195 int total = 0;
196 while (true) {
197 final int read = cis.read(bytes, total, bytes.length - total);
198 if (read < 1) {
199 throw new ImagingException("Could not read block.");
200 }
201 total += read;
202 if (total >= length) {
203 return bytes;
204 }
205 }
206 }
207
208 private Block getFirstBlock() throws IOException {
209 if (null == headBlock) {
210 headBlock = readBlock();
211 }
212 return headBlock;
213 }
214
215 @Override
216 public InputStream getInputStream() throws IOException {
217 return new BlockInputStream();
218 }
219
220 private Block readBlock() throws IOException {
221 if (null == readBuffer) {
222 readBuffer = new byte[BLOCK_SIZE];
223 }
224
225 final int read = inputStream.read(readBuffer);
226 if (read < 1) {
227 return null;
228 }
229 if (read < BLOCK_SIZE) {
230
231 return new Block(Arrays.copyOf(readBuffer, read));
232 }
233
234 final byte[] result = readBuffer;
235 readBuffer = null;
236 return new Block(result);
237 }
238
239 @Override
240 public long size() throws IOException {
241 if (streamLength >= 0) {
242 return streamLength;
243 }
244
245 try (InputStream cis = getInputStream()) {
246 final long result = IOUtils.consume(cis);
247 streamLength = result;
248 return result;
249 }
250 }
251
252 }