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