You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by bo...@apache.org on 2016/10/13 17:06:23 UTC

commons-compress git commit: COMPRESS-327 write support for in memory SeekableByteChannel

Repository: commons-compress
Updated Branches:
  refs/heads/master e926d99b1 -> 09edbd8f1


COMPRESS-327 write support for in memory SeekableByteChannel


Project: http://git-wip-us.apache.org/repos/asf/commons-compress/repo
Commit: http://git-wip-us.apache.org/repos/asf/commons-compress/commit/09edbd8f
Tree: http://git-wip-us.apache.org/repos/asf/commons-compress/tree/09edbd8f
Diff: http://git-wip-us.apache.org/repos/asf/commons-compress/diff/09edbd8f

Branch: refs/heads/master
Commit: 09edbd8f1945cd6a2ecc267f4e466862227e2355
Parents: e926d99
Author: Stefan Bodewig <bo...@apache.org>
Authored: Thu Oct 13 19:05:57 2016 +0200
Committer: Stefan Bodewig <bo...@apache.org>
Committed: Thu Oct 13 19:05:57 2016 +0200

----------------------------------------------------------------------
 .../utils/SeekableInMemoryByteChannel.java      | 69 ++++++++++++++++----
 .../commons/compress/archivers/ZipTestCase.java | 45 +++++++++++++
 .../archivers/sevenz/SevenZOutputFileTest.java  | 26 ++++++++
 3 files changed, 129 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/commons-compress/blob/09edbd8f/src/main/java/org/apache/commons/compress/utils/SeekableInMemoryByteChannel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/compress/utils/SeekableInMemoryByteChannel.java b/src/main/java/org/apache/commons/compress/utils/SeekableInMemoryByteChannel.java
index ab3a606..87c2111 100644
--- a/src/main/java/org/apache/commons/compress/utils/SeekableInMemoryByteChannel.java
+++ b/src/main/java/org/apache/commons/compress/utils/SeekableInMemoryByteChannel.java
@@ -23,21 +23,27 @@ import java.nio.ByteBuffer;
 import java.nio.channels.ClosedChannelException;
 import java.nio.channels.NonWritableChannelException;
 import java.nio.channels.SeekableByteChannel;
+import java.util.Arrays;
 
 /**
  * A {@link SeekableByteChannel} implementation that wraps a byte[].
+ * @since 1.13
  */
 public class SeekableInMemoryByteChannel implements SeekableByteChannel {
 
-    private final byte[] data;
+    private volatile byte[] data;
     private volatile boolean closed;
-    private volatile long position, size;
+    private volatile int position, size;
 
     public SeekableInMemoryByteChannel(byte[] data) {
         this.data = data;
         size = data.length;
     }
 
+    public SeekableInMemoryByteChannel() {
+        this(new byte[0]);
+    }
+
     @Override
     public long position() {
         return position;
@@ -45,7 +51,10 @@ public class SeekableInMemoryByteChannel implements SeekableByteChannel {
 
     @Override
     public SeekableByteChannel position(long newPosition) {
-        position = newPosition;
+        if (newPosition > Integer.MAX_VALUE) {
+            throw new IllegalArgumentException("Position cannot exceed " + Integer.MAX_VALUE);
+        }
+        position = (int) newPosition;
         return this;
     }
 
@@ -56,7 +65,12 @@ public class SeekableInMemoryByteChannel implements SeekableByteChannel {
 
     @Override
     public SeekableByteChannel truncate(long newSize) {
-        size = newSize;
+        if (size > newSize) {
+            size = (int) newSize;
+        }
+        if (position > size) {
+            position = size;
+        }
         return this;
     }
 
@@ -65,14 +79,14 @@ public class SeekableInMemoryByteChannel implements SeekableByteChannel {
         if (!isOpen()) {
             throw new ClosedChannelException();
         }
-        long pos = position;
-        long sz = size;
+        int pos = position;
+        int sz = size;
         int wanted = buf.remaining();
-        long possible = sz - pos;
+        int possible = sz - pos;
         if (wanted > possible) {
-            wanted = (int) possible;
+            wanted = possible;
         }
-        buf.put(data, (int) pos, wanted);
+        buf.put(data, pos, wanted);
         position = pos + wanted;
         return wanted;
     }
@@ -87,9 +101,42 @@ public class SeekableInMemoryByteChannel implements SeekableByteChannel {
         return !closed;
     }
 
-    // TODO implement writing
     @Override
     public int write(ByteBuffer b) throws IOException {
-        throw new NonWritableChannelException();
+        if (!isOpen()) {
+            throw new ClosedChannelException();
+        }
+        int pos = position;
+        int sz = data.length;
+        int wanted = b.remaining();
+        int possibleWithoutResize = sz - pos;
+        if (wanted > possibleWithoutResize) {
+            resize(pos + wanted);
+        }
+        b.get(data, pos, wanted);
+        position = pos + wanted;
+        if (size < position) {
+            size = position;
+        }
+        return wanted;
     }
+
+    /**
+     * Obtains the array backing this channel.
+     */
+    public byte[] array() {
+        return data;
+    }
+
+    private void resize(int newLength) {
+        int len = data.length;
+        if (len <= 0) {
+            len = 1;
+        }
+        while (len < newLength) {
+            len <<= 1;
+        }
+        data = Arrays.copyOf(data, len);
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/09edbd8f/src/test/java/org/apache/commons/compress/archivers/ZipTestCase.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/compress/archivers/ZipTestCase.java b/src/test/java/org/apache/commons/compress/archivers/ZipTestCase.java
index 00015ad..84297ae 100644
--- a/src/test/java/org/apache/commons/compress/archivers/ZipTestCase.java
+++ b/src/test/java/org/apache/commons/compress/archivers/ZipTestCase.java
@@ -41,6 +41,7 @@ import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
 import org.apache.commons.compress.archivers.zip.ZipFile; 	
 import org.apache.commons.compress.archivers.zip.ZipMethod;
 import org.apache.commons.compress.utils.IOUtils;
+import org.apache.commons.compress.utils.SeekableInMemoryByteChannel;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -109,6 +110,50 @@ public final class ZipTestCase extends AbstractTestCase {
     }
 
     /**
+     * Archives 2 files and unarchives it again. If the file length of result
+     * and source is the same, it looks like the operations have worked
+     * @throws Exception
+     */
+    @Test
+    public void testZipArchiveCreationInMemory() throws Exception {
+        final File file1 = getFile("test1.xml");
+        final File file2 = getFile("test2.xml");
+        SeekableInMemoryByteChannel c = new SeekableInMemoryByteChannel();
+        try (ZipArchiveOutputStream os = new ZipArchiveOutputStream(c)) {
+            os.putArchiveEntry(new ZipArchiveEntry("testdata/test1.xml"));
+            IOUtils.copy(new FileInputStream(file1), os);
+            os.closeArchiveEntry();
+
+            os.putArchiveEntry(new ZipArchiveEntry("testdata/test2.xml"));
+            IOUtils.copy(new FileInputStream(file2), os);
+            os.closeArchiveEntry();
+        }
+
+        // Unarchive the same
+        final List<File> results = new ArrayList<>();
+
+        try (ArchiveInputStream in = new ArchiveStreamFactory()
+             .createArchiveInputStream("zip", new ByteArrayInputStream(c.array()))) {
+
+            ZipArchiveEntry entry = null;
+            while((entry = (ZipArchiveEntry)in.getNextEntry()) != null) {
+                final File outfile = new File(resultDir.getCanonicalPath() + "/result/" + entry.getName());
+                outfile.getParentFile().mkdirs();
+                try (OutputStream o = new FileOutputStream(outfile)) {
+                    IOUtils.copy(in, o);
+                }
+                results.add(outfile);
+            }
+        }
+
+        assertEquals(results.size(), 2);
+        File result = results.get(0);
+        assertEquals(file1.length(), result.length());
+        result = results.get(1);
+        assertEquals(file2.length(), result.length());
+    }
+
+    /**
      * Simple unarchive test. Asserts nothing.
      * @throws Exception
      */

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/09edbd8f/src/test/java/org/apache/commons/compress/archivers/sevenz/SevenZOutputFileTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/compress/archivers/sevenz/SevenZOutputFileTest.java b/src/test/java/org/apache/commons/compress/archivers/sevenz/SevenZOutputFileTest.java
index 989aa4d..aee1e02 100644
--- a/src/test/java/org/apache/commons/compress/archivers/sevenz/SevenZOutputFileTest.java
+++ b/src/test/java/org/apache/commons/compress/archivers/sevenz/SevenZOutputFileTest.java
@@ -29,6 +29,7 @@ import java.util.Collections;
 import java.util.Date;
 import java.util.Iterator;
 import org.apache.commons.compress.AbstractTestCase;
+import org.apache.commons.compress.utils.SeekableInMemoryByteChannel;
 import org.tukaani.xz.LZMA2Options;
 
 public class SevenZOutputFileTest extends AbstractTestCase {
@@ -301,6 +302,16 @@ public class SevenZOutputFileTest extends AbstractTestCase {
     }
 
     @Test
+    public void testStackOfContentCompressionsInMemory() throws Exception {
+        final ArrayList<SevenZMethodConfiguration> methods = new ArrayList<>();
+        methods.add(new SevenZMethodConfiguration(SevenZMethod.LZMA2));
+        methods.add(new SevenZMethodConfiguration(SevenZMethod.COPY));
+        methods.add(new SevenZMethodConfiguration(SevenZMethod.DEFLATE));
+        methods.add(new SevenZMethodConfiguration(SevenZMethod.BZIP2));
+        createAndReadBack(new SeekableInMemoryByteChannel(), methods);
+    }
+
+    @Test
     public void testDeflateWithConfiguration() throws Exception {
         output = new File(dir, "deflate-options.7z");
         // Deflater.BEST_SPEED
@@ -464,6 +475,21 @@ public class SevenZOutputFileTest extends AbstractTestCase {
         }
     }
 
+    private void createAndReadBack(final SeekableInMemoryByteChannel output, final Iterable<SevenZMethodConfiguration> methods) throws Exception {
+        final SevenZOutputFile outArchive = new SevenZOutputFile(output);
+        outArchive.setContentMethods(methods);
+        try {
+            addFile(outArchive, 0, true);
+        } finally {
+            outArchive.close();
+        }
+        try (SevenZFile archive =
+             new SevenZFile(new SeekableInMemoryByteChannel(output.array()), "in memory",
+                            null)) {
+            assertEquals(Boolean.TRUE, verifyFile(archive, 0, methods));
+        }
+    }
+
     private static void assertContentMethodsEquals(final Iterable<? extends SevenZMethodConfiguration> expected,
                                                    final Iterable<? extends SevenZMethodConfiguration> actual) {
         assertNotNull(actual);