Skip to content

Commit ef1268b

Browse files
committed
Add WriteBufferDataHandle
1 parent b878119 commit ef1268b

File tree

2 files changed

+315
-0
lines changed

2 files changed

+315
-0
lines changed
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
/*
2+
* #%L
3+
* SciJava Common shared library for SciJava software.
4+
* %%
5+
* Copyright (C) 2009 - 2017 Board of Regents of the University of
6+
* Wisconsin-Madison, Broad Institute of MIT and Harvard, Max Planck
7+
* Institute of Molecular Cell Biology and Genetics, University of
8+
* Konstanz, and KNIME GmbH.
9+
* %%
10+
* Redistribution and use in source and binary forms, with or without
11+
* modification, are permitted provided that the following conditions are met:
12+
*
13+
* 1. Redistributions of source code must retain the above copyright notice,
14+
* this list of conditions and the following disclaimer.
15+
* 2. Redistributions in binary form must reproduce the above copyright notice,
16+
* this list of conditions and the following disclaimer in the documentation
17+
* and/or other materials provided with the distribution.
18+
*
19+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
23+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29+
* POSSIBILITY OF SUCH DAMAGE.
30+
* #L%
31+
*/
32+
33+
package org.scijava.io.handle;
34+
35+
import java.io.EOFException;
36+
import java.io.IOException;
37+
38+
import org.scijava.io.location.Location;
39+
40+
/**
41+
* Wraps a {@link DataHandle} and acts as a write buffer.
42+
*
43+
* @author Gabriel Einsdorf
44+
*/
45+
public class WriteBufferDataHandle extends AbstractHigherOrderHandle<Location> {
46+
47+
private static final int DEFAULT_BUFFERSIZE = 10_000;
48+
private long offset = 0;
49+
private int nextPos = 0;
50+
51+
private byte[] buffer;
52+
private final int bufferSize;
53+
54+
/**
55+
* Creates a {@link WriteBufferDataHandle} that wraps the given
56+
* {@link DataHandle}, the default size for the buffer is used
57+
* ({@value #DEFAULT_BUFFERSIZE} bytes).
58+
*
59+
* @param handle the handle to wrap
60+
*/
61+
public WriteBufferDataHandle(final DataHandle<Location> handle) {
62+
this(handle, DEFAULT_BUFFERSIZE);
63+
}
64+
65+
/**
66+
* Creates a {@link WriteBufferDataHandle} that wraps the given
67+
* {@link DataHandle}
68+
*
69+
* @param handle the handle to wrap
70+
* @param bufferSize the size of the write buffer in bytes
71+
*/
72+
public WriteBufferDataHandle(final DataHandle<Location> handle,
73+
final int bufferSize)
74+
{
75+
super(handle);
76+
this.bufferSize = bufferSize;
77+
}
78+
79+
@Override
80+
public void write(final int b) throws IOException {
81+
ensureOpen();
82+
// if buffer is full flush
83+
if (nextPos >= buffer.length) {
84+
flush();
85+
}
86+
// buffer the byte
87+
buffer[nextPos] = (byte) b;
88+
nextPos++;
89+
offset++;
90+
}
91+
92+
@Override
93+
public void write(final byte[] b, final int off, final int len)
94+
throws IOException
95+
{
96+
ensureOpen();
97+
// ensure the range is valid
98+
if ((off < 0) || (off > b.length) || (len < 0) || ((off +
99+
len) > b.length) || ((off + len) < 0))
100+
{
101+
throw new IndexOutOfBoundsException();
102+
}
103+
else if (len == 0) {
104+
return; // nothing to do
105+
}
106+
107+
// skip the buffering and write directly to the handle
108+
if (len > buffer.length) {
109+
flush();
110+
handle().write(b, off, len);
111+
offset += len;
112+
return;
113+
}
114+
115+
// copy to buffer / flush if necessary
116+
int start = off;
117+
final int total = off + len;
118+
while (start < total) {
119+
final int numItems = Math.min(buffer.length - nextPos, total - start);
120+
System.arraycopy(b, start, buffer, nextPos, numItems);
121+
start += numItems;
122+
nextPos += numItems;
123+
if (nextPos >= buffer.length) {
124+
flush();
125+
}
126+
}
127+
}
128+
129+
/**
130+
* Write the buffer content to the underlying handle
131+
*/
132+
private void flush() throws IOException {
133+
ensureOpen();
134+
if (nextPos == 0) return;
135+
136+
handle().write(buffer, 0, nextPos);
137+
nextPos = 0;
138+
}
139+
140+
@Override
141+
public long length() throws IOException {
142+
// data written out + data in the buffer
143+
return handle().length() + nextPos - 1;
144+
}
145+
146+
@Override
147+
public void setLength(final long length) throws IOException {
148+
ensureOpen();
149+
handle().setLength(length);
150+
}
151+
152+
@Override
153+
public boolean isReadable() {
154+
return false;
155+
}
156+
157+
/**
158+
* @throws IOException if this handle has been closed
159+
*/
160+
@Override
161+
protected void ensureOpen() throws IOException {
162+
super.ensureOpen();
163+
if (buffer == null) {
164+
buffer = new byte[bufferSize];
165+
}
166+
}
167+
168+
@Override
169+
public long offset() throws IOException {
170+
return offset;
171+
}
172+
173+
@Override
174+
public void seek(final long pos) throws IOException {
175+
ensureOpen();
176+
if (pos >= length()) {
177+
throw new EOFException();
178+
}
179+
flush();
180+
offset = pos;
181+
handle().seek(offset);
182+
}
183+
184+
@Override
185+
public long skip(final long n) throws IOException {
186+
throw new IOException("Operation 'skip' is not supported!");
187+
}
188+
189+
@Override
190+
public byte readByte() throws IOException {
191+
throw DataHandles.writeOnlyException();
192+
}
193+
194+
@Override
195+
public int read(final byte[] b, final int off, final int len)
196+
throws IOException
197+
{
198+
throw DataHandles.writeOnlyException();
199+
}
200+
201+
@Override
202+
protected void cleanup() throws IOException {
203+
flush();
204+
buffer = null;
205+
}
206+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*
2+
* #%L
3+
* SciJava Common shared library for SciJava software.
4+
* %%
5+
* Copyright (C) 2009 - 2017 Board of Regents of the University of
6+
* Wisconsin-Madison, Broad Institute of MIT and Harvard, Max Planck
7+
* Institute of Molecular Cell Biology and Genetics, University of
8+
* Konstanz, and KNIME GmbH.
9+
* %%
10+
* Redistribution and use in source and binary forms, with or without
11+
* modification, are permitted provided that the following conditions are met:
12+
*
13+
* 1. Redistributions of source code must retain the above copyright notice,
14+
* this list of conditions and the following disclaimer.
15+
* 2. Redistributions in binary form must reproduce the above copyright notice,
16+
* this list of conditions and the following disclaimer in the documentation
17+
* and/or other materials provided with the distribution.
18+
*
19+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
23+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29+
* POSSIBILITY OF SUCH DAMAGE.
30+
* #L%
31+
*/
32+
33+
package org.scijava.io.handle;
34+
35+
import static org.junit.Assert.assertEquals;
36+
37+
import java.io.ByteArrayOutputStream;
38+
import java.io.IOException;
39+
40+
import org.junit.Test;
41+
import org.scijava.Context;
42+
import org.scijava.io.location.BytesLocation;
43+
import org.scijava.io.location.Location;
44+
45+
public class WriteBufferDataHandleTest extends DataHandleTest {
46+
47+
private Location loc;
48+
49+
@Override
50+
public Class<? extends DataHandle<?>> getExpectedHandleType() {
51+
// not needed
52+
return null;
53+
}
54+
55+
@Override
56+
@Test
57+
public void testDataHandle() throws IOException {
58+
final Context context = new Context(DataHandleService.class);
59+
final DataHandleService dataHandleService = context.service(
60+
DataHandleService.class);
61+
62+
loc = createLocation();
63+
try (final DataHandle<Location> handle = //
64+
dataHandleService.create(loc);
65+
final DataHandle<Location> buffer = //
66+
new WriteBufferDataHandle(handle))
67+
{
68+
checkWrites(buffer);
69+
}
70+
}
71+
72+
@Override
73+
public Location createLocation() throws IOException {
74+
75+
final ByteArrayOutputStream out = new ByteArrayOutputStream();
76+
populateData(out);
77+
return new BytesLocation(out.toByteArray());
78+
}
79+
80+
@Override
81+
protected <L extends Location> void checkWrites(final DataHandle<L> handle)
82+
throws IOException
83+
{
84+
final byte[] copy = BYTES.clone();
85+
86+
// change the data
87+
handle.seek(7);
88+
final String splice = "there";
89+
for (int i = 0; i < splice.length(); i++) {
90+
final char c = splice.charAt(i);
91+
handle.write(c);
92+
copy[7 + i] = (byte) c;
93+
}
94+
handle.close();
95+
96+
final Context context = new Context(DataHandleService.class);
97+
final DataHandleService dataHandleService = context.service(
98+
DataHandleService.class);
99+
100+
try (final DataHandle<? extends Location> readHandle = //
101+
dataHandleService.create(loc))
102+
{
103+
readHandle.seek(0);
104+
for (int i = 0; i < copy.length; i++) {
105+
assertEquals(msg(i), 0xff & copy[i], readHandle.read());
106+
}
107+
}
108+
}
109+
}

0 commit comments

Comments
 (0)