Skip to content

Commit 41fb0e5

Browse files
authored
Merge pull request #173 from saalfeldlab/segmented-readdata
Add SegmentedReadData (and other ReadData improvements)
2 parents e95b31f + f7c09f2 commit 41fb0e5

14 files changed

Lines changed: 1342 additions & 126 deletions

src/main/java/org/janelia/saalfeldlab/n5/KeyValueAccessReadData.java

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public class KeyValueAccessReadData implements ReadData {
1717
private final long offset;
1818
private long length;
1919

20-
public KeyValueAccessReadData(LazyRead lazyRead) {
20+
public KeyValueAccessReadData(final LazyRead lazyRead) {
2121
this(lazyRead, 0, -1);
2222
}
2323

@@ -28,11 +28,13 @@ public KeyValueAccessReadData(LazyRead lazyRead) {
2828
}
2929

3030
@Override
31-
public ReadData materialize() throws N5IOException {
32-
if (materialized == null)
33-
materialized = lazyRead.materialize(offset, length);
34-
return materialized;
35-
}
31+
public ReadData materialize() throws N5IOException {
32+
if (materialized == null) {
33+
materialized = lazyRead.materialize(offset, length);
34+
length = materialized.length();
35+
}
36+
return this;
37+
}
3638

3739
/**
3840
* Returns a {@link ReadData} whose length is limited to the given value.
@@ -50,39 +52,44 @@ public ReadData materialize() throws N5IOException {
5052
public ReadData slice(final long offset, final long length) throws N5IOException {
5153
if (offset < 0)
5254
throw new IndexOutOfBoundsException("Negative offset: " + offset);
53-
55+
5456
if (materialized != null)
5557
return materialized.slice(offset, length);
5658

5759
// if a slice of indeterminate length is requested, but the
5860
// length is already known, use the known length;
59-
final int lengthArg;
61+
final long lengthArg;
6062
if (this.length > 0 && length < 0)
61-
lengthArg = (int)(this.length - offset);
63+
lengthArg = this.length - offset;
6264
else
63-
lengthArg = (int)length;
65+
lengthArg = length;
6466

6567
return new KeyValueAccessReadData(lazyRead, this.offset + offset, lengthArg);
6668
}
6769

6870
@Override
6971
public InputStream inputStream() throws N5IOException, IllegalStateException {
70-
return materialize().inputStream();
72+
materialize();
73+
return materialized.inputStream();
7174
}
7275

7376
@Override
7477
public byte[] allBytes() throws N5IOException, IllegalStateException {
75-
return materialize().allBytes();
78+
materialize();
79+
return materialized.allBytes();
7680
}
7781

7882
@Override
79-
public long length() throws N5IOException {
80-
if (materialized != null)
81-
return materialized.length();
82-
if (length < 0) {
83-
length = lazyRead.size() - offset;
84-
}
83+
public long length() {
8584
return length;
8685
}
8786

87+
@Override
88+
public long requireLength() throws N5IOException {
89+
if (length < 0) {
90+
length = lazyRead.size() - offset;
91+
}
92+
return length;
93+
}
94+
8895
}

src/main/java/org/janelia/saalfeldlab/n5/readdata/AbstractInputStreamReadData.java

Lines changed: 0 additions & 71 deletions
This file was deleted.

src/main/java/org/janelia/saalfeldlab/n5/readdata/ByteArrayReadData.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,15 +65,20 @@ public long length() {
6565
return length;
6666
}
6767

68+
@Override
69+
public long requireLength() {
70+
return length;
71+
}
72+
6873
@Override
6974
public InputStream inputStream() {
75+
7076
return new ByteArrayInputStream(data, offset, length);
7177
}
7278

7379
@Override
7480
public byte[] allBytes() {
7581

76-
// alternatively, we could always return the requested length
7782
if (offset == 0 && data.length == length) {
7883
return data;
7984
} else {

src/main/java/org/janelia/saalfeldlab/n5/readdata/InputStreamReadData.java

Lines changed: 60 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@
66
* %%
77
* Redistribution and use in source and binary forms, with or without
88
* modification, are permitted provided that the following conditions are met:
9-
*
9+
*
1010
* 1. Redistributions of source code must retain the above copyright notice,
1111
* this list of conditions and the following disclaimer.
1212
* 2. Redistributions in binary form must reproduce the above copyright notice,
1313
* this list of conditions and the following disclaimer in the documentation
1414
* and/or other materials provided with the distribution.
15-
*
15+
*
1616
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
1717
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1818
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
@@ -28,10 +28,14 @@
2828
*/
2929
package org.janelia.saalfeldlab.n5.readdata;
3030

31+
import java.io.DataInputStream;
32+
import java.io.IOException;
3133
import java.io.InputStream;
34+
import org.apache.commons.io.IOUtils;
35+
import org.janelia.saalfeldlab.n5.N5Exception.N5IOException;
3236

3337
// not thread-safe
34-
class InputStreamReadData extends AbstractInputStreamReadData {
38+
class InputStreamReadData implements ReadData {
3539

3640
private final InputStream inputStream;
3741
private final int length;
@@ -43,18 +47,67 @@ class InputStreamReadData extends AbstractInputStreamReadData {
4347

4448
@Override
4549
public long length() {
46-
return length;
50+
return (bytes != null) ? bytes.length() : length;
51+
}
52+
53+
@Override
54+
public long requireLength() throws N5IOException {
55+
long l = length();
56+
if ( l >= 0 ) {
57+
return l;
58+
} else {
59+
return materialize().length();
60+
}
61+
}
62+
63+
@Override
64+
public ReadData slice(final long offset, final long length) throws N5IOException {
65+
materialize();
66+
return bytes.slice(offset, length);
4767
}
4868

4969
private boolean inputStreamCalled = false;
5070

5171
@Override
5272
public InputStream inputStream() throws IllegalStateException {
53-
if (inputStreamCalled) {
54-
throw new IllegalStateException("InputStream() already called");
55-
} else {
73+
if (bytes != null) {
74+
return bytes.inputStream();
75+
} else if (!inputStreamCalled) {
5676
inputStreamCalled = true;
5777
return inputStream;
78+
} else {
79+
throw new IllegalStateException("InputStream() already called");
80+
}
81+
}
82+
83+
@Override
84+
public byte[] allBytes() throws N5IOException, IllegalStateException {
85+
materialize();
86+
return bytes.allBytes();
87+
}
88+
89+
private ByteArrayReadData bytes;
90+
91+
@Override
92+
public ReadData materialize() throws N5IOException {
93+
if (bytes == null) {
94+
final byte[] data;
95+
if (length >= 0) {
96+
data = new byte[length];
97+
try (InputStream is = inputStream()) {
98+
new DataInputStream(is).readFully(data);
99+
} catch (IOException e) {
100+
throw new N5IOException(e);
101+
}
102+
} else {
103+
try (InputStream is = inputStream()) {
104+
data = IOUtils.toByteArray(is);
105+
} catch (IOException e) {
106+
throw new N5IOException(e);
107+
}
108+
}
109+
bytes = new ByteArrayReadData(data);
58110
}
111+
return this;
59112
}
60113
}

src/main/java/org/janelia/saalfeldlab/n5/readdata/LazyReadData.java

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import java.io.IOException;
3333
import java.io.InputStream;
3434
import java.io.OutputStream;
35+
import org.apache.commons.io.output.CountingOutputStream;
3536
import org.apache.commons.io.output.ProxyOutputStream;
3637
import org.janelia.saalfeldlab.n5.N5Exception.N5IOException;
3738

@@ -42,13 +43,13 @@ class LazyReadData implements ReadData {
4243
}
4344

4445
/**
45-
* Construct a {@code LazyReadData} that uses the given {@code OutputStreamEncoder} to
46+
* Construct a {@code LazyReadData} that uses the given {@code OutputStreamOperator} to
4647
* encode the given {@code ReadData}.
4748
*
4849
* @param data
4950
* the ReadData to encode
5051
* @param encoder
51-
* OutputStreamEncoder to use for encoding
52+
* OutputStreamOperator to use for encoding
5253
*/
5354
LazyReadData(final ReadData data, final OutputStreamOperator encoder) {
5455
this(outputStream -> {
@@ -62,30 +63,53 @@ class LazyReadData implements ReadData {
6263

6364
private ByteArrayReadData bytes;
6465

66+
private long length = -1;
67+
6568
@Override
6669
public ReadData materialize() throws N5IOException {
6770
if (bytes == null) {
68-
final ByteArrayOutputStream baos = new ByteArrayOutputStream(8192);
69-
writeTo(baos);
70-
bytes = new ByteArrayReadData(baos.toByteArray());
71+
try {
72+
final ByteArrayOutputStream baos = new ByteArrayOutputStream(8192);
73+
writer.writeTo(baos);
74+
bytes = new ByteArrayReadData(baos.toByteArray());
75+
} catch (IOException e) {
76+
throw new N5IOException(e);
77+
}
7178
}
72-
return bytes;
79+
return this;
80+
}
81+
82+
@Override
83+
public long length() {
84+
return (bytes != null) ? bytes.length() : length;
7385
}
7486

7587
@Override
76-
public long length() throws N5IOException {
88+
public long requireLength() throws N5IOException {
89+
long l = length();
90+
if ( l >= 0 ) {
91+
return l;
92+
} else {
93+
return materialize().length();
94+
}
95+
}
7796

78-
return materialize().length();
97+
@Override
98+
public ReadData slice(final long offset, final long length) throws N5IOException {
99+
materialize();
100+
return bytes.slice(offset, length);
79101
}
80102

81103
@Override
82104
public InputStream inputStream() throws N5IOException, IllegalStateException {
83-
return materialize().inputStream();
105+
materialize();
106+
return bytes.inputStream();
84107
}
85108

86109
@Override
87110
public byte[] allBytes() throws N5IOException, IllegalStateException {
88-
return materialize().allBytes();
111+
materialize();
112+
return bytes.allBytes();
89113
}
90114

91115
@Override
@@ -94,7 +118,9 @@ public void writeTo(final OutputStream outputStream) throws N5IOException, Illeg
94118
if (bytes != null) {
95119
outputStream.write(bytes.allBytes());
96120
} else {
97-
writer.writeTo(outputStream);
121+
final CountingOutputStream cos = new CountingOutputStream(outputStream);
122+
writer.writeTo(cos);
123+
length = cos.getByteCount();
98124
}
99125
} catch (IOException e) {
100126
throw new N5IOException(e);
@@ -105,7 +131,7 @@ public void writeTo(final OutputStream outputStream) throws N5IOException, Illeg
105131
* {@code UnaryOperator} that wraps {@code OutputStream} to intercept {@code
106132
* close()} and call {@code flush()} instead
107133
*/
108-
private static OutputStreamOperator interceptClose = o -> new ProxyOutputStream(o) {
134+
private static final OutputStreamOperator interceptClose = o -> new ProxyOutputStream(o) {
109135

110136
@Override
111137
public void close() throws IOException {

0 commit comments

Comments
 (0)