Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DataHandle: findString(..) add support handles with unknown length #339

Merged
merged 2 commits into from
Oct 16, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 10 additions & 12 deletions src/main/java/org/scijava/io/handle/DataHandle.java
Original file line number Diff line number Diff line change
Expand Up @@ -350,10 +350,7 @@ default String findString(final boolean saveString, final int blockSize,
final StringBuilder out = new StringBuilder();
final long startPos = offset();
long bytesDropped = 0;
final long inputLen = length();
long maxLen = inputLen - startPos;
final boolean tooLong = saveString && maxLen > MAX_SEARCH_SIZE;
if (tooLong) maxLen = MAX_SEARCH_SIZE;
final long maxLen = saveString ? MAX_SEARCH_SIZE : Long.MAX_VALUE;
boolean match = false;
int maxTermLen = 0;
for (final String term : terminators) {
Expand All @@ -366,7 +363,10 @@ default String findString(final boolean saveString, final int blockSize,
new DataHandleInputStream<>(this), getEncoding());
final char[] buf = new char[blockSize];
long loc = 0;
while (loc < maxLen && offset() < length() - 1) {
int r = 0;

// NB: we need at least 2 bytes to read a char
while (loc < maxLen && ((r = in.read(buf, 0, blockSize)) > 1)) {
// if we're not saving the string, drop any old, unnecessary output
if (!saveString) {
final int outLen = out.length();
Expand All @@ -378,16 +378,12 @@ default String findString(final boolean saveString, final int blockSize,
bytesDropped += dropIndex;
}
}

// read block from stream
final int r = in.read(buf, 0, blockSize);
if (r <= 0) throw new IOException("Cannot read from stream: " + r);

// append block to output
out.append(buf, 0, r);

// check output, returning smallest possible string
int min = Integer.MAX_VALUE, tagLen = 0;
int min = Integer.MAX_VALUE;
int tagLen = 0;
for (final String t : terminators) {
final int len = t.length();
final int start = (int) (loc - bytesDropped - len);
Expand Down Expand Up @@ -415,7 +411,9 @@ default String findString(final boolean saveString, final int blockSize,
}

// no match
if (tooLong) throw new IOException("Maximum search length reached.");
if (loc > MAX_SEARCH_SIZE) {
throw new IOException("Maximum search length reached.");
}
return saveString ? out.toString() : null;
}

Expand Down
142 changes: 142 additions & 0 deletions src/test/java/org/scijava/io/handle/DataHandleEdgeCaseTests.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*-
* #%L
* SciJava Common shared library for SciJava software.
* %%
* Copyright (C) 2009 - 2018 Board of Regents of the University of
* Wisconsin-Madison, Broad Institute of MIT and Harvard, Max Planck
* Institute of Molecular Cell Biology and Genetics, University of
* Konstanz, and KNIME GmbH.
* %%
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
* #L%
*/

package org.scijava.io.handle;

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;

import java.io.IOException;
import java.util.Arrays;

import org.junit.Test;
import org.scijava.Context;
import org.scijava.io.location.BytesLocation;
import org.scijava.io.location.Location;

/**
* Additional Tests for edge case behavior of {@link DataHandle}.
*
* @author Gabriel Einsdorf
*/
public class DataHandleEdgeCaseTests {

private static final byte[] BYTES = { //
'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '\n', //
9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, -128, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, //
125, 127, -127, -125, -3, 'h', 'e' };

/**
* Test to ensure {@link DataHandle#findString(String...)} and
* {@link DataHandle#readCString()} work with {@link DataHandle}
* implementations that have unknown length.
*
* @throws IOException
*/
@Test
public void testFindStringsOnUnknonwLengthHandle() throws IOException {
final Context ctx = new Context();
final DataHandleService dhs = ctx.getService(DataHandleService.class);
final DummyHandle dummy = new DummyHandle(dhs.create(new BytesLocation(
BYTES)));

assertEquals("Hello,", dummy.findString(","));
assertEquals(" world\n", dummy.findString("\n"));

dummy.seek(41);
assertEquals("he", dummy.findString("\n"));

dummy.seek(16);
assertArrayEquals(Arrays.copyOfRange(BYTES, 16, 23), dummy.readCString()
.getBytes());
dummy.seek(42);
assertNull(dummy.readCString());
}

private class DummyHandle extends AbstractHigherOrderHandle<Location> {

public DummyHandle(final DataHandle<Location> handle) {
super(handle);
}

@Override
public long length() throws IOException {
return -1;
}

@Override
public long offset() throws IOException {
return handle().offset();
}

@Override
public void seek(final long pos) throws IOException {
handle().seek(pos);
}

@Override
public void setLength(final long length) throws IOException {
handle().setLength(length);
}

@Override
public int read(final byte[] b, final int off, final int len)
throws IOException
{
return handle().read(b, off, len);
}

@Override
public byte readByte() throws IOException {
return handle().readByte();
}

@Override
public void write(final int b) throws IOException {
handle().write(b);
}

@Override
public void write(final byte[] b, final int off, final int len)
throws IOException
{
handle().write(b, off, len);
}

@Override
protected void cleanup() throws IOException {
//
}
}

}