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