package de.samaflost.commons_compress;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.Arrays;

import org.apache.commons.compress.archivers.ArchiveInputStream;
import org.apache.commons.compress.archivers.ArchiveStreamFactory;
import org.apache.commons.compress.archivers.sevenz.SevenZFile;
import org.apache.commons.compress.utils.IOUtils;
import org.apache.commons.compress.utils.SeekableInMemoryByteChannel;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;

@State(Scope.Benchmark)
public class UnarchivingBenchmark {

    private byte[] SMALL_FILE;
    private byte[] MANY_SMALL_FILES;
    private byte[] BIGGER_FILE;
    private static final ArchiveStreamFactory factory = new ArchiveStreamFactory();

    @Param({"zip"})
    public String format;

    @Setup
    public void readData() throws Exception {
        SMALL_FILE = ArchivingBenchmark.archive(TestFixture.SMALL_FILE, format, 1);
        BIGGER_FILE = ArchivingBenchmark.archive(TestFixture.BIGGER_FILE, format, 1);
        MANY_SMALL_FILES = ArchivingBenchmark.archive(TestFixture.SMALL_FILE, format, 10000);
    }

    @Benchmark
    public byte[] unarchiveSmallFile() throws Exception {
        return unarchive(SMALL_FILE);
    }

    @Benchmark
    public byte[] unarchiveManySmallFiles() throws Exception {
        return unarchive(MANY_SMALL_FILES);
    }

    @Benchmark
    public byte[] archiveBiggerFile() throws Exception {
        return unarchive(BIGGER_FILE);
    }

    private byte[] unarchive(byte[] data) throws Exception {
        switch(format) {
        case ArchiveStreamFactory.SEVEN_Z:
            return unarchive7z(data);
        default:
            return unarchiveStream(data);
        }
    }

    private byte[] unarchiveStream(byte[] data) throws Exception {
        try (ByteArrayInputStream in = new ByteArrayInputStream(data);
             ArchiveInputStream ain = factory.createArchiveInputStream(format, in);
             ) {
            byte[] lastEntryData = null;
            while (ain.getNextEntry() != null) {
                try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
                    IOUtils.copy(ain, out);
                    out.close();
                    lastEntryData = out.toByteArray();
                }
            }
            return lastEntryData;
        }
    }

    private byte[] unarchive7z(byte[] data) throws Exception {
        try (SeekableInMemoryByteChannel ch = new SeekableInMemoryByteChannel(data);
             SevenZFile ain = new SevenZFile(ch);
             ) {
            final byte[] buffer = new byte[8192];
            byte[] lastEntryData = null;
            while (ain.getNextEntry() != null) {
                try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
                    int n = 0;
                    while (-1 != (n = ain.read(buffer))) {
                        out.write(buffer, 0, n);
                    }
                    out.close();
                    lastEntryData = out.toByteArray();
                }
            }
            return lastEntryData;
        }
    }
}

@@ -1644,32 +1644,185 @@
 R_FILE);%0A    %7D%0A%0A
+    @Benchmark%0A    public byte%5B%5D unarchiveManySmallFilesMetaDataOnly() throws Exception %7B%0A        return unarchiveMetadataOnly(MANY_SMALL_FILES);%0A    %7D%0A%0A
     private byte
@@ -3437,11 +3437,992 @@
  %7D%0A    %7D
+%0A%0A    private byte%5B%5D unarchiveMetadataOnly(byte%5B%5D data) throws Exception %7B%0A        switch(format) %7B%0A        case ArchiveStreamFactory.SEVEN_Z:%0A            return unarchive7zMetadataOnly(data);%0A        default:%0A            return unarchiveStreamMetadataOnly(data);%0A        %7D%0A    %7D%0A%0A    private byte%5B%5D unarchiveStreamMetadataOnly(byte%5B%5D data) throws Exception %7B%0A        try (ByteArrayInputStream in = new ByteArrayInputStream(data);%0A             ArchiveInputStream ain = factory.createArchiveInputStream(format, in);%0A             ) %7B%0A            while (ain.getNextEntry() != null) %7B%0A            %7D%0A            return new byte%5B0%5D;%0A        %7D%0A    %7D%0A%0A    private byte%5B%5D unarchive7zMetadataOnly(byte%5B%5D data) throws Exception %7B%0A        try (SeekableInMemoryByteChannel ch = new SeekableInMemoryByteChannel(data);%0A             SevenZFile ain = new SevenZFile(ch);%0A             ) %7B%0A            while (ain.getNextEntry() != null) %7B%0A            %7D%0A            return new byte%5B0%5D;%0A        %7D%0A    %7D
 %0A%7D%0A

add a test for only reading metadata of an archive