-
Notifications
You must be signed in to change notification settings - Fork 53
Add copy method for handles #306
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,13 +9,13 @@ | |
| * %% | ||
| * 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 | ||
|
|
@@ -38,10 +38,14 @@ | |
| import java.lang.reflect.InvocationTargetException; | ||
| import java.lang.reflect.Method; | ||
|
|
||
| import org.scijava.io.location.Location; | ||
| import org.scijava.task.Task; | ||
|
|
||
| /** | ||
| * Utility methods for working with {@link DataHandle}s. | ||
| * | ||
| * | ||
| * @author Curtis Rueden | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add yourself as another author. |
||
| * @author Gabriel Einsdorf | ||
| */ | ||
| public final class DataHandles { | ||
|
|
||
|
|
@@ -108,4 +112,141 @@ private static synchronized void initUTFMethod() { | |
| "No usable DataOutputStream.writeUTF(String, DataOutput)", exc); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Copies all bytes from the input to the output handle. Reading and writing | ||
| * start at the current positions of the handles. | ||
| * | ||
| * @param in the input handle | ||
| * @param out the output handle | ||
| * @return the number of bytes copied | ||
| * @throws IOException if an I/O error occurs. | ||
| */ | ||
| public static long copy(final DataHandle<Location> in, | ||
| final DataHandle<Location> out) throws IOException | ||
| { | ||
| return copy(in, out, 0l, null); | ||
| } | ||
|
|
||
| /** | ||
| * Copies all bytes from the input to the output handle, reporting the | ||
| * progress to the provided task. Reading and writing start at the current | ||
| * positions of the handles. | ||
| * | ||
| * @param in the input handle | ||
| * @param out the output handle | ||
| * @param task task to report progress to | ||
| * @return the number of bytes copied | ||
| * @throws IOException if an I/O error occurs. | ||
| */ | ||
| public static long copy(final DataHandle<Location> in, | ||
| final DataHandle<Location> out, final Task task) throws IOException | ||
| { | ||
| return copy(in, out, 0l, task); | ||
| } | ||
|
|
||
| /** | ||
| * Copies up to <code>length</code> bytes from the input to the output handle. | ||
| * Reading and writing start at the current positions of the handles. Stops | ||
| * early if there are no more bytes available from the input handle. | ||
| * | ||
| * @param in the input handle | ||
| * @param out the output handle | ||
| * @param length maximum number of bytes to copy; will copy all bytes if set | ||
| * to <code>0</code> | ||
| * @return the number of bytes copied | ||
| * @throws IOException if an I/O error occurs. | ||
| */ | ||
| public static long copy(final DataHandle<Location> in, | ||
| final DataHandle<Location> out, final int length) throws IOException | ||
| { | ||
| return copy(in, out, length, null); | ||
| } | ||
|
|
||
| /** | ||
| * Copies up to <code>length</code> bytes from the input to the output handle, | ||
| * reporting the progress to the provided task. Reading and writing start at | ||
| * the current positions of the handles. Stops early if there are no more | ||
| * bytes available from the input handle. | ||
| * | ||
| * @param in input handle | ||
| * @param out the output handle | ||
| * @param length maximum number of bytes to copy; will copy all bytes if set | ||
| * to <code>0</code> | ||
| * @param task a task object to use for reporting the status of the copy | ||
| * operation. Can be <code>null</code> if no reporting is needed. | ||
| * @return the number of bytes copied | ||
| * @throws IOException if an I/O error occurs. | ||
| */ | ||
| public static long copy(final DataHandle<Location> in, | ||
| final DataHandle<Location> out, final long length, final Task task) | ||
| throws IOException | ||
| { | ||
| return copy(in, out, length, task, 64 * 1024); | ||
| } | ||
|
|
||
| /** | ||
| * Copies up to <code>length</code> bytes from the input to the output handle, | ||
| * reporting the progress to the provided task. Reading and writing start at | ||
| * the current positions of the handles. Stops early if there are no more | ||
| * bytes available from the input handle. Uses a buffer of the provided size, | ||
| * instead of using the default size. | ||
| * | ||
| * @param in input handle | ||
| * @param out the output handle | ||
| * @param length maximum number of bytes to copy, will copy all bytes if set | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. More grammatically correct would be |
||
| * to <code>0</code> | ||
| * @param task a task object to use for reporting the status of the copy | ||
| * operation. Can be <code>null</code> if no reporting is needed. | ||
| * @return the number of bytes copied | ||
| * @throws IOException if an I/O error occurs. | ||
| */ | ||
| public static long copy(final DataHandle<Location> in, | ||
| final DataHandle<Location> out, final long length, final Task task, | ||
| final int bufferSize) throws IOException | ||
| { | ||
|
|
||
| // get length of input | ||
| final long inputlength; | ||
| { | ||
| long i = 0; | ||
| try { | ||
| i = in.length(); | ||
| } | ||
| catch (final IOException exc) { | ||
| // Assume unknown length. | ||
| i = 0; | ||
| } | ||
| inputlength = i; | ||
| } | ||
|
|
||
| if (task != null) { | ||
| if (length > 0) task.setProgressMaximum(length); | ||
| else if (inputlength > 0) task.setProgressMaximum(inputlength); | ||
| } | ||
|
|
||
| final byte[] buffer = new byte[bufferSize]; | ||
| long totalRead = 0; | ||
|
|
||
| while (true) { | ||
| if (task != null && task.isCanceled()) break; | ||
| final int r; | ||
| // ensure we do not read more than required into the buffer | ||
| if (length > 0 && totalRead + bufferSize > length) { | ||
| int remaining = (int) (length - totalRead); | ||
| r = in.read(buffer, 0, remaining); | ||
| } | ||
| else { | ||
| r = in.read(buffer); | ||
| } | ||
| if (r <= 0) break; // EOF | ||
| if (task != null && task.isCanceled()) break; | ||
| out.write(buffer, 0, r); | ||
| totalRead += r; | ||
| if (task != null) { | ||
| task.setProgressValue(task.getProgressValue() + r); | ||
| } | ||
| } | ||
| return totalRead; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,152 @@ | ||
| /*- | ||
| * #%L | ||
| * SciJava Common shared library for SciJava software. | ||
| * %% | ||
| * Copyright (C) 2009 - 2017 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% | ||
| */ | ||
|
|
||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing license header. |
||
| package org.scijava.io.handle; | ||
|
|
||
| import static org.junit.Assert.assertEquals; | ||
|
|
||
| import java.io.IOException; | ||
|
|
||
| import org.junit.After; | ||
| import org.junit.Before; | ||
| import org.junit.Test; | ||
| import org.scijava.Context; | ||
| import org.scijava.event.EventService; | ||
| import org.scijava.io.location.BytesLocation; | ||
| import org.scijava.io.location.Location; | ||
| import org.scijava.task.DefaultTask; | ||
| import org.scijava.task.Task; | ||
| import org.scijava.thread.ThreadService; | ||
| import org.scijava.util.MersenneTwisterFast; | ||
|
|
||
| /** | ||
| * Tests for utility methods in the {@link DataHandles} class. | ||
| * | ||
| * @author Gabriel Einsdorf | ||
| */ | ||
| public class DataHandlesTest { | ||
|
|
||
| private static final int TEST_SIZE = 2_938_740; | ||
| private DataHandleService handles; | ||
| private Location inFile; | ||
| private BytesLocation outFile; | ||
| private byte[] data; | ||
| private ThreadService threadService; | ||
| private EventService eventService; | ||
|
|
||
| @Before | ||
| public void classSetup() { | ||
| final Context ctx = new Context(DataHandleService.class, | ||
| ThreadService.class, EventService.class); | ||
| handles = ctx.service(DataHandleService.class); | ||
| threadService = ctx.service(ThreadService.class); | ||
| eventService = ctx.service(EventService.class); | ||
|
|
||
| data = randomBytes(0xbabebabe); | ||
| inFile = new BytesLocation(data); | ||
| outFile = new BytesLocation(TEST_SIZE); | ||
| } | ||
|
|
||
| @After | ||
| public void cleanup() { | ||
| handles.context().dispose(); | ||
| } | ||
|
|
||
| @Test | ||
| public void testDefaultCopy() throws IOException { | ||
| try (DataHandle<Location> src = handles.create(inFile); | ||
| final DataHandle<Location> dest = handles.create(outFile)) | ||
| { | ||
| DataHandles.copy(src, dest); | ||
| assertHandleEquals(data, dest); | ||
| } | ||
| } | ||
|
|
||
| @Test | ||
| public void testCopyTask() throws IOException { | ||
| try (DataHandle<Location> src = handles.create(inFile); | ||
| final DataHandle<Location> dest = handles.create(outFile)) | ||
| { | ||
| final Task t = new DefaultTask(threadService, eventService); | ||
| DataHandles.copy(src, dest, t); | ||
| assertEquals(t.getProgressValue(), src.length()); | ||
| assertHandleEquals(data, dest); | ||
| } | ||
| } | ||
|
|
||
| @Test | ||
| public void testCopyLength() throws IOException { | ||
| final int sliceSize = 50_000; | ||
| try (DataHandle<Location> src = handles.create(inFile); | ||
| final DataHandle<Location> dest = handles.create(outFile)) | ||
| { | ||
| DataHandles.copy(src, dest, sliceSize); | ||
| final byte[] expected = new byte[sliceSize]; | ||
| System.arraycopy(data, 0, expected, 0, sliceSize); | ||
| assertHandleEquals(expected, dest); | ||
| } | ||
| } | ||
|
|
||
| @Test | ||
| public void testCopyLengthTask() throws IOException { | ||
| final int sliceSize = 50_000; | ||
| try (DataHandle<Location> src = handles.create(inFile); | ||
| final DataHandle<Location> dest = handles.create(outFile)) | ||
| { | ||
| final Task t = new DefaultTask(threadService, eventService); | ||
| DataHandles.copy(src, dest, sliceSize, t); | ||
| assertEquals(t.getProgressValue(), sliceSize); | ||
|
|
||
| final byte[] expected = new byte[sliceSize]; | ||
| System.arraycopy(data, 0, expected, 0, sliceSize); | ||
| assertHandleEquals(expected, dest); | ||
| } | ||
| } | ||
|
|
||
| private void assertHandleEquals(final byte[] expected, | ||
| final DataHandle<Location> handle) throws IOException | ||
| { | ||
| handle.seek(0); | ||
| for (int i = 0; i < expected.length; i++) { | ||
| assertEquals(expected[i], handle.readByte()); | ||
| } | ||
| } | ||
|
|
||
| private byte[] randomBytes(final long seed) { | ||
| final MersenneTwisterFast r = new MersenneTwisterFast(seed); | ||
| final byte[] ldata = new byte[TEST_SIZE]; | ||
| for (int i = 0; i < ldata.length; i++) { | ||
| ldata[i] = r.nextByte(); | ||
| } | ||
| return ldata; | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 👍