diff --git a/src/main/java/org/scijava/io/handle/AbstractDataHandle.java b/src/main/java/org/scijava/io/handle/AbstractDataHandle.java index 2d2258ab2..f5e5f1c59 100644 --- a/src/main/java/org/scijava/io/handle/AbstractDataHandle.java +++ b/src/main/java/org/scijava/io/handle/AbstractDataHandle.java @@ -44,6 +44,13 @@ public abstract class AbstractDataHandle extends AbstractWrapperPlugin implements DataHandle { + private byte[] conversionBuffer = new byte[8]; + + @Override + public byte[] conversionBuffer() { + return conversionBuffer; + } + // -- Fields -- private ByteOrder order = ByteOrder.BIG_ENDIAN; diff --git a/src/main/java/org/scijava/io/handle/DataHandle.java b/src/main/java/org/scijava/io/handle/DataHandle.java index db48caeee..ccb93d434 100644 --- a/src/main/java/org/scijava/io/handle/DataHandle.java +++ b/src/main/java/org/scijava/io/handle/DataHandle.java @@ -43,6 +43,7 @@ import org.scijava.io.location.Location; import org.scijava.plugin.WrapperPlugin; +import org.scijava.util.Bytes; /** * A data handle is a plugin which provides both streaming and random @@ -249,6 +250,11 @@ default void setLittleEndian(final boolean little) { /** Sets the native encoding of the stream. */ void setEncoding(String encoding); + /** + * @return a 8 byte long buffer array used for type conversions + */ + byte[] conversionBuffer(); + /** Reads a string of arbitrary length, terminated by a null char. */ default String readCString() throws IOException { final String line = findString("\0"); @@ -514,18 +520,10 @@ default int readUnsignedByte() throws IOException { @Override default short readShort() throws IOException { - final int ch0; - final int ch1; - if (isBigEndian()) { - ch0 = read(); - ch1 = read(); - } - else { - ch1 = read(); - ch0 = read(); - } - if ((ch0 | ch1) < 0) throw new EOFException(); - return (short) ((ch0 << 8) + (ch1 << 0)); + final byte[] buf = conversionBuffer(); + final int read = read(buf, 0, 2); + if (read < 2) throw new EOFException(); + return Bytes.toShort(buf, isLittleEndian()); } @Override @@ -540,68 +538,20 @@ default char readChar() throws IOException { @Override default int readInt() throws IOException { - final int ch0; - final int ch1; - final int ch2; - final int ch3; - if (isBigEndian()) { - ch0 = read(); - ch1 = read(); - ch2 = read(); - ch3 = read(); - } - else { - ch3 = read(); - ch2 = read(); - ch1 = read(); - ch0 = read(); - } - if ((ch0 | ch1 | ch2 | ch3) < 0) throw new EOFException(); - return ((ch0 << 24) + (ch1 << 16) + (ch2 << 8) + (ch3 << 0)); + final byte[] buf = conversionBuffer(); + final int read = read(buf, 0, 4); + if (read < 4) throw new EOFException(); + return Bytes.toInt(buf, isLittleEndian()); } @Override default long readLong() throws IOException { - final int ch0; - final int ch1; - final int ch2; - final int ch3; - final int ch4; - final int ch5; - final int ch6; - final int ch7; - if (isBigEndian()) { - ch0 = read(); - ch1 = read(); - ch2 = read(); - ch3 = read(); - ch4 = read(); - ch5 = read(); - ch6 = read(); - ch7 = read(); - } - else { - ch7 = read(); - ch6 = read(); - ch5 = read(); - ch4 = read(); - ch3 = read(); - ch2 = read(); - ch1 = read(); - ch0 = read(); - } - if ((ch0 | ch1 | ch2 | ch3 | ch4 | ch5 | ch6 | ch7) < 0) { + final byte[] buf = conversionBuffer(); + final int read = read(buf, 0, 8); + if (read < 8) { throw new EOFException(); } - // TODO: Double check this inconsistent code. - return ((long) ch0 << 56) + // - ((long) (ch1 & 255) << 48) + // - ((long) (ch2 & 255) << 40) + // - ((long) (ch3 & 255) << 32) + // - ((long) (ch4 & 255) << 24) + // - ((ch5 & 255) << 16) + // - ((ch6 & 255) << 8) + // - ((ch7 & 255) << 0); + return Bytes.toLong(buf, isLittleEndian()); } @Override @@ -618,7 +568,7 @@ default double readDouble() throws IOException { default String readLine() throws IOException { // NB: Adapted from java.io.RandomAccessFile.readLine(). - final StringBuffer input = new StringBuffer(); + final StringBuilder input = new StringBuilder(); int c = -1; boolean eol = false; @@ -669,76 +619,42 @@ default void writeByte(final int v) throws IOException { @Override default void writeShort(final int v) throws IOException { - if (isBigEndian()) { - write((v >>> 8) & 0xFF); - write((v >>> 0) & 0xFF); - } - else { - write((v >>> 0) & 0xFF); - write((v >>> 8) & 0xFF); - } + final byte[] buf = conversionBuffer(); + Bytes.unpack(v, buf, 0, 2, isLittleEndian()); + write(buf, 0, 2); } @Override default void writeChar(final int v) throws IOException { - if (isBigEndian()) { - write((v >>> 8) & 0xFF); - write((v >>> 0) & 0xFF); - } - else { - write((v >>> 0) & 0xFF); - write((v >>> 8) & 0xFF); - } + writeShort(v); } @Override default void writeInt(final int v) throws IOException { - if (isBigEndian()) { - write((v >>> 24) & 0xFF); - write((v >>> 16) & 0xFF); - write((v >>> 8) & 0xFF); - write((v >>> 0) & 0xFF); - } - else { - write((v >>> 0) & 0xFF); - write((v >>> 8) & 0xFF); - write((v >>> 16) & 0xFF); - write((v >>> 24) & 0xFF); - } + final byte[] buf = conversionBuffer(); + Bytes.unpack(v, buf, 0, 4, isLittleEndian()); + write(buf, 0, 4); } @Override default void writeLong(final long v) throws IOException { - if (isBigEndian()) { - write((byte) (v >>> 56)); - write((byte) (v >>> 48)); - write((byte) (v >>> 40)); - write((byte) (v >>> 32)); - write((byte) (v >>> 24)); - write((byte) (v >>> 16)); - write((byte) (v >>> 8)); - write((byte) (v >>> 0)); - } - else { - write((byte) (v >>> 0)); - write((byte) (v >>> 8)); - write((byte) (v >>> 16)); - write((byte) (v >>> 24)); - write((byte) (v >>> 32)); - write((byte) (v >>> 40)); - write((byte) (v >>> 48)); - write((byte) (v >>> 56)); - } + final byte[] buf = conversionBuffer(); + Bytes.unpack(v, buf, 0, 8, isLittleEndian()); + write(buf, 0, 8); } @Override default void writeFloat(final float v) throws IOException { - writeInt(Float.floatToIntBits(v)); + final byte[] buf = conversionBuffer(); + Bytes.unpack(Float.floatToIntBits(v), buf, 0, 4, isLittleEndian()); + write(buf, 0, 4); } @Override default void writeDouble(final double v) throws IOException { - writeLong(Double.doubleToLongBits(v)); + final byte[] buf = conversionBuffer(); + Bytes.unpack(Double.doubleToLongBits(v), buf, 0, 8, isLittleEndian()); + write(buf, 0, 8); } @Override @@ -750,9 +666,7 @@ default void writeBytes(final String s) throws IOException { default void writeChars(final String s) throws IOException { final int len = s.length(); for (int i = 0; i < len; i++) { - final int v = s.charAt(i); - write((v >>> 8) & 0xFF); - write((v >>> 0) & 0xFF); + writeChar(s.charAt(i)); } } diff --git a/src/main/java/org/scijava/io/handle/FileHandle.java b/src/main/java/org/scijava/io/handle/FileHandle.java index 87fad4386..a42e05587 100644 --- a/src/main/java/org/scijava/io/handle/FileHandle.java +++ b/src/main/java/org/scijava/io/handle/FileHandle.java @@ -151,21 +151,6 @@ public byte readByte() throws IOException { return reader().readByte(); } - @Override - public char readChar() throws IOException { - return reader().readChar(); - } - - @Override - public double readDouble() throws IOException { - return reader().readDouble(); - } - - @Override - public float readFloat() throws IOException { - return reader().readFloat(); - } - @Override public void readFully(final byte[] b) throws IOException { reader().readFully(b); @@ -178,36 +163,16 @@ public void readFully(final byte[] b, final int off, final int len) reader().readFully(b, off, len); } - @Override - public int readInt() throws IOException { - return reader().readInt(); - } - @Override public String readLine() throws IOException { return reader().readLine(); } - @Override - public long readLong() throws IOException { - return reader().readLong(); - } - - @Override - public short readShort() throws IOException { - return reader().readShort(); - } - @Override public int readUnsignedByte() throws IOException { return reader().readUnsignedByte(); } - @Override - public int readUnsignedShort() throws IOException { - return reader().readUnsignedShort(); - } - @Override public String readUTF() throws IOException { return reader().readUTF(); @@ -252,41 +217,11 @@ public void writeBytes(final String s) throws IOException { writer().writeBytes(s); } - @Override - public void writeChar(final int v) throws IOException { - writer().writeChar(v); - } - @Override public void writeChars(final String s) throws IOException { writer().writeChars(s); } - @Override - public void writeDouble(final double v) throws IOException { - writer().writeDouble(v); - } - - @Override - public void writeFloat(final float v) throws IOException { - writer().writeFloat(v); - } - - @Override - public void writeInt(final int v) throws IOException { - writer().writeInt(v); - } - - @Override - public void writeLong(final long v) throws IOException { - writer().writeLong(v); - } - - @Override - public void writeShort(final int v) throws IOException { - writer().writeShort(v); - } - @Override public void writeUTF(final String str) throws IOException { writer().writeUTF(str); diff --git a/src/test/java/org/scijava/io/handle/DataHandleTest.java b/src/test/java/org/scijava/io/handle/DataHandleTest.java index f15248c7c..7c38e3e1a 100644 --- a/src/test/java/org/scijava/io/handle/DataHandleTest.java +++ b/src/test/java/org/scijava/io/handle/DataHandleTest.java @@ -33,16 +33,19 @@ package org.scijava.io.handle; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import java.io.IOException; import java.io.OutputStream; import java.util.Arrays; +import java.util.function.Supplier; +import org.junit.Before; import org.junit.Test; import org.scijava.Context; -import org.scijava.io.handle.DataHandle; import org.scijava.io.handle.DataHandle.ByteOrder; -import org.scijava.io.handle.DataHandleService; import org.scijava.io.location.Location; import org.scijava.util.Bytes; @@ -55,29 +58,78 @@ public abstract class DataHandleTest { protected 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, -2, -1 }; + 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, -2, -1 }; - // -- Test methods -- + protected DataHandleService dataHandleService; - @Test - public void testDataHandle() throws IOException { + @Before + public void init() { final Context context = new Context(DataHandleService.class); - final DataHandleService dataHandleService = - context.service(DataHandleService.class); + dataHandleService = context.service(DataHandleService.class); + } + + @Test + public void checkSkip() throws IOException { + try (DataHandle handle = createHandle()) { + handle.seek(0); + handle.skip(10); + assertEquals(10, handle.offset()); + handle.skipBytes(11); + assertEquals(21, handle.offset()); + } + } + + @Test + public void testEndianesSettings() throws IOException { + + try (DataHandle handle = createHandle()) { + final ByteOrder original = handle.getOrder(); - final Location loc = createLocation(); - try (final DataHandle handle = // - dataHandleService.create(loc)) - { - assertEquals(getExpectedHandleType(), handle.getClass()); + handle.setOrder(ByteOrder.BIG_ENDIAN); + assertEquals(ByteOrder.BIG_ENDIAN, handle.getOrder()); + assertTrue(handle.isBigEndian()); + assertFalse(handle.isLittleEndian()); - checkReads(handle); - checkWrites(handle); + handle.setOrder(ByteOrder.LITTLE_ENDIAN); + assertEquals(ByteOrder.LITTLE_ENDIAN, handle.getOrder()); + assertFalse(handle.isBigEndian()); + assertTrue(handle.isLittleEndian()); + + handle.setLittleEndian(false); + assertEquals(ByteOrder.BIG_ENDIAN, handle.getOrder()); + assertTrue(handle.isBigEndian()); + assertFalse(handle.isLittleEndian()); + + handle.setLittleEndian(true); + assertEquals(ByteOrder.LITTLE_ENDIAN, handle.getOrder()); + assertFalse(handle.isBigEndian()); + assertTrue(handle.isLittleEndian()); + + handle.setOrder(original); + } + } + + @Test + public void testReading() throws IOException { + try (final DataHandle handle = createHandle()) { + checkBasicReadMethods(handle); + checkEndiannessReading(handle); } } - // -- DataHandleTest methods -- + @Test + public void testWriting() throws IOException { + try (DataHandle handle = createHandle()) { + checkBasicWriteMethods(handle); + final Location loc = createLocation(); + checkWriteEndianes(() -> dataHandleService.create(loc), + ByteOrder.LITTLE_ENDIAN); + checkWriteEndianes(() -> dataHandleService.create(loc), + ByteOrder.BIG_ENDIAN); + checkAdvancedStringWriting(() -> dataHandleService.create(loc)); + } + } public abstract Class> getExpectedHandleType(); @@ -85,19 +137,45 @@ public void testDataHandle() throws IOException { // -- Internal methods -- - protected void populateData(final OutputStream out) throws IOException { + /** + * Creates a handle for testing + */ + public DataHandle createHandle() { + Location loc; + try { + loc = createLocation(); + } + catch (final IOException exc) { + throw new RuntimeException(exc); + } + final DataHandle handle = dataHandleService.create(loc); + assertEquals(getExpectedHandleType(), handle.getClass()); + return handle; + } + + /** + * Populates the provided {@link OutputStream} with test data. + * + * @param out the {@link OutputStream} to fill + * @throws IOException + */ + public void populateData(final OutputStream out) throws IOException { out.write(BYTES); out.close(); } - protected void checkReads(final DataHandle handle) - throws IOException + /** + * Checks basic byte reading methods. + * + * @param handle the handle to test + * @throws IOException + */ + public void checkBasicReadMethods( + final DataHandle handle) throws IOException { assertEquals(0, handle.offset()); assertEquals(BYTES.length, handle.length()); assertEquals("UTF-8", handle.getEncoding()); - assertEquals(ByteOrder.BIG_ENDIAN, handle.getOrder()); - assertEquals(false, handle.isLittleEndian()); // test read() for (int i = 0; i < BYTES.length; i++) { @@ -119,98 +197,362 @@ protected void checkReads(final DataHandle handle) assertEquals(msg(i), BYTES[i], handle.readByte()); } + // test readUnsignedByte() + handle.seek(0); + for (int i = 0; i < BYTES.length; i++) { + assertEquals(msg(i), BYTES[i] & 0xff, handle.readUnsignedByte()); + } + + // test readFully(byte[]) + Arrays.fill(buf, (byte) 0); + handle.seek(3); + handle.readFully(buf); + assertBytesMatch(3, buf.length, buf); + + // test readCString() - _includes_ the null terminator! + handle.seek(16); + assertBytesMatch(16, 7, handle.readCString().getBytes()); + handle.seek(42); + assertNull(handle.readCString()); + + // test readBoolean + handle.seek(21); + assertTrue(handle.readBoolean()); + assertFalse(handle.readBoolean()); + + // test readLine() - _excludes_ the newline terminator! + handle.seek(7); + assertBytesMatch(7, 5, handle.readLine().getBytes()); + + // test readString(String) - _includes_ the matching terminator! + handle.seek(7); + assertBytesMatch(7, 5, handle.readString("abcdefg").getBytes()); + + // test readString() + handle.seek(7); + assertBytesMatch(7, 5, handle.readString("d").getBytes()); + + // test readString(int + handle.seek(7); + assertBytesMatch(7, 5, handle.readString(5).getBytes()); + + // test findString(String) - _includes_ the matching terminator! + handle.seek(1); + assertBytesMatch(1, 11, handle.findString("world").getBytes()); + + handle.seek(0); + handle.findString(false, "world"); + assertEquals(12, handle.offset()); + + handle.seek(0); + handle.findString(false, "w"); + assertEquals(8, handle.offset()); + } + + /** + * Checks reading methods effected by endianness. Tests both + * {@link ByteOrder#LITTLE_ENDIAN} and {@link ByteOrder#BIG_ENDIAN}. + * + * @param handle the handle to check + * @throws IOException + */ + public void checkEndiannessReading( + final DataHandle handle) throws IOException + { + checkEndiannessReading(handle, ByteOrder.LITTLE_ENDIAN); + checkEndiannessReading(handle, ByteOrder.BIG_ENDIAN); + } + + /** + * Checks reading methods effected by endianness. + * + * @param handle the handle to check + * @param order the {@link ByteOrder} to check + * @throws IOException + */ + public void checkEndiannessReading( + final DataHandle handle, final ByteOrder order) + throws IOException + { + handle.setOrder(order); + handle.seek(0); + final boolean little = order == ByteOrder.LITTLE_ENDIAN; + + // test readChar() + + handle.seek(0); + for (int i = 0; i < BYTES.length / 2; i += 2) { + assertEquals(msg(i), (char) Bytes.toShort(BYTES, i, little), handle + .readChar()); + } + // test readShort() handle.seek(0); for (int i = 0; i < BYTES.length / 2; i += 2) { - assertEquals(msg(i), Bytes.toShort(BYTES, i, false), handle.readShort()); + assertEquals(msg(i), Bytes.toShort(BYTES, i, little), handle.readShort()); } // test readInt() handle.seek(0); for (int i = 0; i < BYTES.length / 4; i += 4) { - assertEquals(msg(i), Bytes.toInt(BYTES, i, false), handle.readInt()); + assertEquals(msg(i), Bytes.toInt(BYTES, i, little), handle.readInt()); } // test readLong() handle.seek(0); for (int i = 0; i < BYTES.length / 8; i += 8) { - assertEquals(msg(i), Bytes.toLong(BYTES, i, false), handle.readLong()); + assertEquals(msg(i), Bytes.toLong(BYTES, i, little), handle.readLong()); } // test readFloat() handle.seek(0); for (int i = 0; i < BYTES.length / 4; i += 4) { - assertEquals(msg(i), Bytes.toFloat(BYTES, i, false), handle.readFloat(), + assertEquals(msg(i), Bytes.toFloat(BYTES, i, little), handle.readFloat(), 0); } // test readDouble() handle.seek(0); for (int i = 0; i < BYTES.length / 8; i += 8) { - assertEquals(msg(i), Bytes.toDouble(BYTES, i, false), - handle.readDouble(), 0); - } - - // test readBoolean() - handle.seek(0); - for (int i = 0; i < BYTES.length; i++) { - assertEquals(msg(i), BYTES[i] == 0 ? false : true, handle.readBoolean()); + assertEquals(msg(i), Bytes.toDouble(BYTES, i, little), handle + .readDouble(), 0); } + } - // test readChar() - handle.seek(0); - for (int i = 0; i < BYTES.length / 2; i += 2) { - assertEquals(msg(i), (char) Bytes.toInt(BYTES, i, 2, false), handle - .readChar()); - } - - // test readFully(byte[]) - Arrays.fill(buf, (byte) 0); - handle.seek(3); - handle.readFully(buf); - assertBytesMatch(3, buf.length, buf); - - // test readCString() - _includes_ the null terminator! - handle.seek(16); - assertBytesMatch(16, 7, handle.readCString().getBytes()); - - // test readLine() - _excludes_ the newline terminator! - handle.seek(7); - assertBytesMatch(7, 5, handle.readLine().getBytes()); - - // test readString(String) - _includes_ the matching terminator! - handle.seek(7); - assertBytesMatch(7, 5, handle.readString("abcdefg").getBytes()); - - // test findString(String) - _includes_ the matching terminator! - handle.seek(1); - assertBytesMatch(1, 11, handle.findString("world").getBytes()); + /** + * Check basic write methods for bytes. + * + * @param handle the handle to write to and read from + * @throws IOException + */ + public void checkBasicWriteMethods( + final DataHandle handle) throws IOException + { + checkBasicWrites(handle, handle); } - protected void checkWrites(final DataHandle handle) + /** + * Tests basic write methods for bytes, both provided handles must point to + * the same location! + * + * @param readHandle the handle to read from + * @param writeHandle the handle to write from + * @throws IOException + */ + public void checkBasicWrites( + final DataHandle readHandle, final DataHandle writeHandle) throws IOException { final byte[] copy = BYTES.clone(); // change the data - handle.seek(7); + writeHandle.seek(7); final String splice = "there"; for (int i = 0; i < splice.length(); i++) { final char c = splice.charAt(i); - handle.write(c); + writeHandle.write(c); copy[7 + i] = (byte) c; } + writeHandle.writeBoolean(true); + copy[12] = 1; + writeHandle.writeBoolean(false); + copy[13] = 0; + + writeHandle.writeByte(42); + copy[14] = 42; + + if (writeHandle != readHandle) { + writeHandle.close(); // to ensure data is flushed + } + // verify the changes - handle.seek(0); + readHandle.seek(0); for (int i = 0; i < copy.length; i++) { - assertEquals(msg(i), 0xff & copy[i], handle.read()); + assertEquals(msg(i), 0xff & copy[i], readHandle.read()); + } + } + + /** + * Checks advanced string writing methods. + * + * @param handleCreator a supplier that creates properly initialized handles + * for reading and writing, all created handles must point to the + * same location! + * @throws IOException + */ + public void checkAdvancedStringWriting( + final Supplier> handleCreator) throws IOException + { + checkAdvancedStringWriting(handleCreator, handleCreator); + } + + /** + * Checks advanced string writing methods. + * + * @param readHandleCreator a supplier that creates properly initialized + * handles for reading, all created handles must point to the same + * location! + * @param writeHandleCreator a supplier that creates properly initialized + * handles for reading, all created handles must point to the same + * location! + * @throws IOException + */ + public void checkAdvancedStringWriting( + final Supplier> readHandleCreator, + final Supplier> writeHandleCreator) throws IOException + { + // test writeUTF() / readUTF() + final String utfTestString = "abcäúöäéëåáðßø¶🤓🍕😋"; + try (DataHandle writeHandle = writeHandleCreator.get()) { + writeHandle.writeUTF(utfTestString); + } + try (DataHandle readHandle = readHandleCreator.get()) { + assertEquals(utfTestString, readHandle.readUTF()); + } + + // test writeLine() + final String testString = "The quick brown fox jumps over the lazy dog."; + try (DataHandle writeHandle = writeHandleCreator.get()) { + writeHandle.writeLine(testString); + } + try (DataHandle readHandle = readHandleCreator.get()) { + assertEquals(testString, readHandle.readLine()); + } + + // test writeChars / findString + final String testString2 = "The five boxing wizards jump quickly."; + try (DataHandle writeHandle = writeHandleCreator.get()) { + writeHandle.writeChars(testString2); + } + try (DataHandle readHandle = readHandleCreator.get()) { + for (int i = 0; i < testString2.length(); i++) { + assertEquals(testString2.charAt(i), readHandle.readChar()); + } + } + } + + /** + * Checks writing methods affected by endianness. + * + * @param readHandleCreator a supplier that creates properly initialized + * handles for reading, all created handles must point to the same + * location! + * @param writeHandleCreator a supplier that creates properly initialized + * handles for reading, all created handles must point to the same + * location! + * @throws IOException + */ + public void checkWriteEndianes( + final Supplier> handleCreator, final ByteOrder order) + throws IOException + { + checkWriteEndianes(handleCreator, handleCreator, order); + } + + public void checkWriteEndianes( + final Supplier> readHandleCreator, + final Supplier> writeHandleCreator, final ByteOrder order) + throws IOException + { + final boolean little = order == ByteOrder.LITTLE_ENDIAN; + + // test writeChar() + try (DataHandle writeHandle = writeHandleCreator.get()) { + writeHandle.setOrder(order); + for (int i = 0; i < BYTES.length / 2; i += 2) { + writeHandle.writeChar(Bytes.toInt(BYTES, i, 2, little)); + } + } + + try (DataHandle readHandle = readHandleCreator.get()) { + readHandle.setOrder(order); + for (int i = 0; i < BYTES.length / 2; i += 2) { + assertEquals(msg(i), Bytes.toShort(BYTES, i, little), readHandle + .readChar()); + } + } + + // test writeShort() + try (DataHandle writeHandle = writeHandleCreator.get()) { + writeHandle.setOrder(order); + for (int i = 0; i < BYTES.length / 2; i += 2) { + writeHandle.writeShort(Bytes.toShort(BYTES, i, little)); + } + } + + try (DataHandle readHandle = readHandleCreator.get()) { + readHandle.setOrder(order); + for (int i = 0; i < BYTES.length / 2; i += 2) { + assertEquals(msg(i), Bytes.toShort(BYTES, i, little), readHandle + .readShort()); + } + } + + // test writeInt() + try (DataHandle writeHandle = writeHandleCreator.get()) { + writeHandle.setOrder(order); + for (int i = 0; i < BYTES.length / 4; i += 4) { + writeHandle.writeInt(Bytes.toInt(BYTES, i, little)); + } + } + try (DataHandle readHandle = readHandleCreator.get()) { + readHandle.setOrder(order); + for (int i = 0; i < BYTES.length / 4; i += 4) { + assertEquals(msg(i), Bytes.toInt(BYTES, i, little), readHandle + .readInt()); + } + } + + // test writeLong() + try (DataHandle writeHandle = writeHandleCreator.get()) { + writeHandle.setOrder(order); + for (int i = 0; i < BYTES.length / 8; i += 8) { + writeHandle.writeLong(Bytes.toLong(BYTES, i, little)); + } + } + try (DataHandle readHandle = readHandleCreator.get()) { + readHandle.setOrder(order); + for (int i = 0; i < BYTES.length / 8; i += 8) { + assertEquals(msg(i), Bytes.toLong(BYTES, i, little), readHandle + .readLong()); + } + } + + // test writeFloat() + try (DataHandle writeHandle = writeHandleCreator.get()) { + writeHandle.setOrder(order); + for (int i = 0; i < BYTES.length / 4; i += 4) { + writeHandle.writeFloat(Bytes.toFloat(BYTES, i, little)); + } + } + try (DataHandle readHandle = readHandleCreator.get()) { + readHandle.setOrder(order); + for (int i = 0; i < BYTES.length / 4; i += 4) { + assertEquals(msg(i), Bytes.toFloat(BYTES, i, little), readHandle + .readFloat(), 0); + } + } + + // test writeDouble() + try (DataHandle writeHandle = writeHandleCreator.get()) { + writeHandle.setOrder(order); + for (int i = 0; i < BYTES.length / 8; i += 8) { + writeHandle.writeDouble(Bytes.toDouble(BYTES, i, little)); + } + } + try (DataHandle readHandle = readHandleCreator.get()) { + readHandle.setOrder(order); + for (int i = 0; i < BYTES.length / 8; i += 8) { + assertEquals(msg(i), Bytes.toDouble(BYTES, i, little), readHandle + .readDouble(), 0); + } } } // -- Internal methods -- - protected void assertBytesMatch(final int offset, final int length, + public void assertBytesMatch(final int offset, final int length, final byte[] b) { assertEquals(length, b.length); @@ -219,7 +561,9 @@ protected void assertBytesMatch(final int offset, final int length, } } - protected String msg(final int i) { + // -- Test methods -- + + public String msg(final int i) { return "[" + i + "]:"; } diff --git a/src/test/java/org/scijava/io/handle/ReadBufferDataHandleTest.java b/src/test/java/org/scijava/io/handle/ReadBufferDataHandleTest.java index a2a987a5e..e7cb332f2 100644 --- a/src/test/java/org/scijava/io/handle/ReadBufferDataHandleTest.java +++ b/src/test/java/org/scijava/io/handle/ReadBufferDataHandleTest.java @@ -42,9 +42,8 @@ import java.util.List; import java.util.Random; -import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; -import org.scijava.Context; import org.scijava.io.location.BytesLocation; import org.scijava.io.location.Location; @@ -55,35 +54,40 @@ */ public class ReadBufferDataHandleTest extends DataHandleTest { - private Context context; - private DataHandleService dataHandleService; - - @Override @Test - public void testDataHandle() throws IOException { + public void testSmallBuffer() throws IOException { final Location loc = createLocation(); try (final DataHandle handle = // dataHandleService.create(loc); AbstractDataHandle bufferedHandle = // - new ReadBufferDataHandle(handle)) + new ReadBufferDataHandle(handle, 5)) { - checkReads(bufferedHandle); + // check with small buffersize + checkBasicReadMethods(bufferedHandle); + checkEndiannessReading(bufferedHandle); } } - @Test - public void testSmallBuffer() throws IOException { + @Test(expected = IOException.class) + public void ensureNotWritable() throws IOException { + createHandle().write(1); + } - final Location loc = createLocation(); - try (final DataHandle handle = // - dataHandleService.create(loc); - AbstractDataHandle bufferedHandle = // - new ReadBufferDataHandle(handle, 5)) - { - // check with small buffersize - checkReads(bufferedHandle); + @Override + public DataHandle createHandle() { + Location loc; + try { + loc = createLocation(); } + catch (final IOException exc) { + throw new RuntimeException(exc); + } + final DataHandle handle = // + dataHandleService.create(loc); + final AbstractDataHandle bufferedHandle = // + new ReadBufferDataHandle(handle, 5); + return bufferedHandle; } @Test @@ -91,7 +95,7 @@ public void testLargeRead() throws Exception { final int size = 10_00; final byte[] bytes = new byte[size]; - Random r = new Random(42); + final Random r = new Random(42); r.nextBytes(bytes); final Location loc = new BytesLocation(bytes); @@ -104,34 +108,34 @@ public void testLargeRead() throws Exception { final byte[] actual = new byte[size]; // create evenly sized slice ranges - int slices = 60; - int range = (size + slices - 1) / slices; - List> ranges = new ArrayList<>(); + final int slices = 60; + final int range = (size + slices - 1) / slices; + final List> ranges = new ArrayList<>(); for (int i = 0; i < slices; i++) { - int start = range * i; - int end = range * (i + 1); + final int start = range * i; + final int end = range * (i + 1); ranges.add(new SimpleEntry<>(start, end)); } Collections.shuffle(ranges, r); - for (SimpleEntry e : ranges) { + for (final SimpleEntry e : ranges) { bufferedHandle.seek(e.getKey()); bufferedHandle.read(actual, e.getKey(), e.getValue() - e.getKey()); } - assertArrayEquals(bytes, actual); } } - @Before - public void setup() { - context = new Context(DataHandleService.class); - dataHandleService = context.service(DataHandleService.class); + @Test + @Ignore + @Override + public void testWriting() throws IOException { + // nothing to do here } @Override public Class> getExpectedHandleType() { - return null; + throw new UnsupportedOperationException(); } @Override diff --git a/src/test/java/org/scijava/io/handle/WriteBufferDataHandleTest.java b/src/test/java/org/scijava/io/handle/WriteBufferDataHandleTest.java index b17d4037a..40f035e3a 100644 --- a/src/test/java/org/scijava/io/handle/WriteBufferDataHandleTest.java +++ b/src/test/java/org/scijava/io/handle/WriteBufferDataHandleTest.java @@ -32,78 +32,84 @@ package org.scijava.io.handle; -import static org.junit.Assert.assertEquals; - import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.util.function.Supplier; +import org.junit.Ignore; import org.junit.Test; -import org.scijava.Context; +import org.scijava.io.handle.DataHandle.ByteOrder; import org.scijava.io.location.BytesLocation; import org.scijava.io.location.Location; public class WriteBufferDataHandleTest extends DataHandleTest { - private Location loc; - @Override public Class> getExpectedHandleType() { // not needed - return null; + throw new UnsupportedOperationException(); } @Override - @Test - public void testDataHandle() throws IOException { - final Context context = new Context(DataHandleService.class); - final DataHandleService dataHandleService = context.service( - DataHandleService.class); + public Location createLocation() throws IOException { + // not needed + throw new UnsupportedOperationException(); + } - loc = createLocation(); - try (final DataHandle handle = // - dataHandleService.create(loc); - final DataHandle buffer = // - new WriteBufferDataHandle(handle)) - { - checkWrites(buffer); - } + @Override + public DataHandle createHandle() { + final DataHandle handle = // + dataHandleService.create(new BytesLocation(new byte[42])); + return dataHandleService.writeBuffer(handle); } + @Test + @Ignore @Override - public Location createLocation() throws IOException { + public void testReading() throws IOException { + // nothing to do + } + + @Test + @Ignore + @Override + public void checkSkip() throws IOException { + // nothing to do + } - final ByteArrayOutputStream out = new ByteArrayOutputStream(); - populateData(out); - return new BytesLocation(out.toByteArray()); + @Test(expected = IOException.class) + public void ensureNotReadable() throws IOException { + createHandle().read(); } @Override - protected void checkWrites(final DataHandle handle) - throws IOException - { - final byte[] copy = BYTES.clone(); + @Test + public void testWriting() throws IOException { + final ByteArrayOutputStream os = new ByteArrayOutputStream(42); + populateData(os); + final BytesLocation location = new BytesLocation(os.toByteArray()); + final DataHandle handle = // + dataHandleService.create(location); + final DataHandle writeHandle = dataHandleService.writeBuffer( + handle); - // change the data - handle.seek(7); - final String splice = "there"; - for (int i = 0; i < splice.length(); i++) { - final char c = splice.charAt(i); - handle.write(c); - copy[7 + i] = (byte) c; - } - handle.close(); + checkBasicWrites(handle, writeHandle); + } - final Context context = new Context(DataHandleService.class); - final DataHandleService dataHandleService = context.service( - DataHandleService.class); + @Test + public void testEndiannessWriting() throws IOException { + final BytesLocation location = new BytesLocation(new byte[42]); + final Supplier> readHandleSupplier = + () -> dataHandleService.create(location); + final Supplier> writeHandleSupplier = () -> { + final DataHandle h = dataHandleService.create(location); + return dataHandleService.writeBuffer(h); + }; - try (final DataHandle readHandle = // - dataHandleService.create(loc)) - { - readHandle.seek(0); - for (int i = 0; i < copy.length; i++) { - assertEquals(msg(i), 0xff & copy[i], readHandle.read()); - } - } + checkWriteEndianes(readHandleSupplier, writeHandleSupplier, + ByteOrder.LITTLE_ENDIAN); + checkWriteEndianes(readHandleSupplier, writeHandleSupplier, + ByteOrder.LITTLE_ENDIAN); + checkAdvancedStringWriting(readHandleSupplier, writeHandleSupplier); } }