Skip to content

Commit c424667

Browse files
committed
Migrate SCIFIO I/O framework to SciJava Common
This implementation is a restructuring of SCIFIO's io.scif.io package, to fit within the SciJava plugin framework. This commit introduces a new plugin type, DataHandle, that defines all the methods for random access reading and writing. The DataHandle interface is a rich SciJava plugin type that extends the java.io.DataInput, java.io.DataOutput and java.io.Closeable interfaces. Furthermore, the DataHandle interface also defines additional API methods that make it very straightforward to adapt DataHandle objects into InputStreams and OutputStreams (it cannot extend InputStream and OutputStream, because they are abstract classes, not interfaces). The actual adaptation to streams is done using the DataHandleInputStream and DataHandleOutputStream wrapper objects, simply by delegating to the various DataHandle methods of the same name. Overall, the mapping from the old io.scif.io API is as follows: * IRandomAccess + IStreamAccess -> org.scijava.io.DataHandle * RandomAccessInputStream -> org.scijava.io.DataHandleInputStream * RandomAccessOutputStream -> org.scijava.io.DataHandleOutputStream * Location -> subsumed into the org.scijava.io.Location type hierarchy * HandleException -> unnecessary; IOException et. al are good enough * FileHandle, URLHandle, etc. -> same names, implementing DataHandle Note that the mapping is rather loose, with several layers consolidated or eliminated. Additional commits will readd features, such as specific DataHandle plugins, as appropriate to achieve needed functionality.
1 parent b74d78a commit c424667

File tree

7 files changed

+823
-0
lines changed

7 files changed

+823
-0
lines changed
Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
/*
2+
* #%L
3+
* SciJava Common shared library for SciJava software.
4+
* %%
5+
* Copyright (C) 2009 - 2015 Board of Regents of the University of
6+
* Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck
7+
* Institute of Molecular Cell Biology and Genetics.
8+
* %%
9+
* Redistribution and use in source and binary forms, with or without
10+
* modification, are permitted provided that the following conditions are met:
11+
*
12+
* 1. Redistributions of source code must retain the above copyright notice,
13+
* this list of conditions and the following disclaimer.
14+
* 2. Redistributions in binary form must reproduce the above copyright notice,
15+
* this list of conditions and the following disclaimer in the documentation
16+
* and/or other materials provided with the distribution.
17+
*
18+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
22+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28+
* POSSIBILITY OF SUCH DAMAGE.
29+
* #L%
30+
*/
31+
32+
package org.scijava.io;
33+
34+
import java.io.IOException;
35+
import java.io.InputStreamReader;
36+
import java.nio.ByteBuffer;
37+
import java.nio.ByteOrder;
38+
39+
import org.scijava.plugin.AbstractWrapperPlugin;
40+
41+
/**
42+
* Abstract base class for {@link DataHandle} plugins.
43+
*
44+
* @author Curtis Rueden
45+
*/
46+
public abstract class AbstractDataHandle<L extends Location> extends
47+
AbstractWrapperPlugin<L> implements DataHandle<L>
48+
{
49+
50+
// -- Constants --
51+
52+
/** Block size to use when searching through the stream. */
53+
private static final int DEFAULT_BLOCK_SIZE = 256 * 1024; // 256 KB
54+
55+
/** Maximum number of bytes to search when searching through the stream. */
56+
private static final int MAX_SEARCH_SIZE = 512 * 1024 * 1024; // 512 MB
57+
58+
// -- Fields --
59+
60+
private ByteOrder order = ByteOrder.BIG_ENDIAN;
61+
private String encoding = "UTF-8";
62+
63+
// -- DataHandle methods --
64+
65+
@Override
66+
public ByteOrder getOrder() {
67+
return order;
68+
}
69+
70+
@Override
71+
public boolean isLittleEndian() {
72+
return getOrder() == ByteOrder.LITTLE_ENDIAN;
73+
}
74+
75+
@Override
76+
public void setOrder(final ByteOrder order) {
77+
this.order = order;
78+
}
79+
80+
@Override
81+
public void setOrder(final boolean little) {
82+
setOrder(little ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN);
83+
}
84+
85+
@Override
86+
public String getEncoding() {
87+
return encoding;
88+
}
89+
90+
@Override
91+
public void setEncoding(final String encoding) {
92+
this.encoding = encoding;
93+
}
94+
95+
@Override
96+
public int read(final ByteBuffer buf) throws IOException {
97+
return read(buf, buf.remaining());
98+
}
99+
100+
@Override
101+
public int read(final ByteBuffer buf, final int len)
102+
throws IOException
103+
{
104+
final int n;
105+
if (buf.hasArray()) {
106+
// read directly into the array
107+
n = read(buf.array(), buf.arrayOffset(), len);
108+
}
109+
else {
110+
// read into a temporary array, then copy
111+
final byte[] b = new byte[len];
112+
n = read(b);
113+
buf.put(b, 0, n);
114+
}
115+
return n;
116+
}
117+
118+
@Override
119+
public void write(final ByteBuffer buf) throws IOException {
120+
write(buf, buf.remaining());
121+
}
122+
123+
@Override
124+
public void write(final ByteBuffer buf, final int len)
125+
throws IOException
126+
{
127+
if (buf.hasArray()) {
128+
// write directly from the buffer's array
129+
write(buf.array(), buf.arrayOffset(), len);
130+
}
131+
else {
132+
// copy into a temporary array, then write
133+
final byte[] b = new byte[len];
134+
buf.get(b);
135+
write(b);
136+
}
137+
}
138+
139+
@Override
140+
public String readCString() throws IOException {
141+
final String line = findString("\0");
142+
return line.length() == 0 ? null : line;
143+
}
144+
145+
@Override
146+
public String readString(int n) throws IOException {
147+
final long avail = length() - offset();
148+
if (n > avail) n = (int) avail;
149+
final byte[] b = new byte[n];
150+
readFully(b);
151+
return new String(b, encoding);
152+
}
153+
154+
@Override
155+
public String readString(final String lastChars) throws IOException {
156+
if (lastChars.length() == 1) return findString(lastChars);
157+
final String[] terminators = new String[lastChars.length()];
158+
for (int i = 0; i < terminators.length; i++) {
159+
terminators[i] = lastChars.substring(i, i + 1);
160+
}
161+
return findString(terminators);
162+
}
163+
164+
@Override
165+
public String findString(final String... terminators) throws IOException {
166+
return findString(true, DEFAULT_BLOCK_SIZE, terminators);
167+
}
168+
169+
@Override
170+
public String findString(final boolean saveString,
171+
final String... terminators) throws IOException
172+
{
173+
return findString(saveString, DEFAULT_BLOCK_SIZE, terminators);
174+
}
175+
176+
@Override
177+
public String findString(final int blockSize, final String... terminators)
178+
throws IOException
179+
{
180+
return findString(true, blockSize, terminators);
181+
}
182+
183+
@Override
184+
public String findString(final boolean saveString, final int blockSize,
185+
final String... terminators) throws IOException
186+
{
187+
final StringBuilder out = new StringBuilder();
188+
final long startPos = offset();
189+
long bytesDropped = 0;
190+
final long inputLen = length();
191+
long maxLen = inputLen - startPos;
192+
final boolean tooLong = saveString && maxLen > MAX_SEARCH_SIZE;
193+
if (tooLong) maxLen = MAX_SEARCH_SIZE;
194+
boolean match = false;
195+
int maxTermLen = 0;
196+
for (final String term : terminators) {
197+
final int len = term.length();
198+
if (len > maxTermLen) maxTermLen = len;
199+
}
200+
201+
@SuppressWarnings("resource")
202+
final InputStreamReader in =
203+
new InputStreamReader(new DataHandleInputStream<L>(this), getEncoding());
204+
final char[] buf = new char[blockSize];
205+
long loc = 0;
206+
while (loc < maxLen && offset() < length() - 1) {
207+
// if we're not saving the string, drop any old, unnecessary output
208+
if (!saveString) {
209+
final int outLen = out.length();
210+
if (outLen >= maxTermLen) {
211+
final int dropIndex = outLen - maxTermLen + 1;
212+
final String last = out.substring(dropIndex, outLen);
213+
out.setLength(0);
214+
out.append(last);
215+
bytesDropped += dropIndex;
216+
}
217+
}
218+
219+
// read block from stream
220+
final int r = in.read(buf, 0, blockSize);
221+
if (r <= 0) throw new IOException("Cannot read from stream: " + r);
222+
223+
// append block to output
224+
out.append(buf, 0, r);
225+
226+
// check output, returning smallest possible string
227+
int min = Integer.MAX_VALUE, tagLen = 0;
228+
for (final String t : terminators) {
229+
final int len = t.length();
230+
final int start = (int) (loc - bytesDropped - len);
231+
final int value = out.indexOf(t, start < 0 ? 0 : start);
232+
if (value >= 0 && value < min) {
233+
match = true;
234+
min = value;
235+
tagLen = len;
236+
}
237+
}
238+
239+
if (match) {
240+
// reset stream to proper location
241+
seek(startPos + bytesDropped + min + tagLen);
242+
243+
// trim output string
244+
if (saveString) {
245+
out.setLength(min + tagLen);
246+
return out.toString();
247+
}
248+
return null;
249+
}
250+
251+
loc += r;
252+
}
253+
254+
// no match
255+
if (tooLong) throw new IOException("Maximum search length reached.");
256+
return saveString ? out.toString() : null;
257+
}
258+
259+
// -- InputStream look-alikes --
260+
261+
@Override
262+
public int read(byte[] b) throws IOException {
263+
return read(b, 0, b.length);
264+
}
265+
266+
@Override
267+
public long skip(final long n) throws IOException {
268+
if (n < 0) return 0;
269+
final long remain = length() - offset();
270+
final long num = n < remain ? n : remain;
271+
seek(offset() + num);
272+
return num;
273+
}
274+
275+
}

0 commit comments

Comments
 (0)