added net.woggioni.jwo.compression.CompressionOutputStream and net.woggioni.jwo.compression.CompressionInputStream

This commit is contained in:
Walter Oggioni
2019-12-22 00:51:42 +01:00
committed by Walter Oggioni
parent 4a5bcea903
commit 762dfae586
11 changed files with 55071 additions and 1 deletions

View File

@@ -13,6 +13,7 @@ import java.security.cert.X509Certificate;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
@@ -351,4 +352,12 @@ public class JWO {
"Unknown value '%s' for enum %s", value, cls.getName());
return result;
}
public static Optional<Path> which(String command) {
return Stream.of(System.getenv("PATH").split(Pattern.quote(File.pathSeparator)))
.map(path -> Paths.get(path, command))
.filter(Files::exists)
.filter(Files::isExecutable)
.findFirst();
}
}

View File

@@ -0,0 +1,17 @@
package net.woggioni.jwo.compression;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public enum CompressionFormat {
XZ("xz", "xz"),
BZIP2("bzip2", "bz2"),
GZIP("gzip", "gz"),
LZMA("lzma", "lzma"),
LZO("lzop", "lzo"),
LZ4("lz4", "lz4"),
ZSTD("zstd", "zst");
public final String executable;
public final String suffix;
}

View File

@@ -0,0 +1,71 @@
package net.woggioni.jwo.compression;
import lombok.SneakyThrows;
import java.io.IOException;
import java.io.InputStream;
public class CompressionInputStream extends InputStream {
private final InputStream inputStream;
private final Process process;
private final Thread writer;
private final InputStream processOutput;
public CompressionInputStream(InputStream is, CompressionFormat compressionFormat) {
this(is, compressionFormat, StreamMode.COMPRESSION);
}
public CompressionInputStream(InputStream is, CompressionFormat compressionFormat, Integer level) {
this(is, compressionFormat, StreamMode.COMPRESSION, level);
}
public CompressionInputStream(InputStream is, CompressionFormat compressionFormat, StreamMode mode) {
this(is, compressionFormat, mode, null);
}
@SneakyThrows
public CompressionInputStream(InputStream is, CompressionFormat compressionFormat, StreamMode mode, Integer level) {
inputStream = is;
String[] cliArgs;
switch(mode) {
case COMPRESSION:
if(level == null) {
cliArgs = new String[] {compressionFormat.executable};
} else {
cliArgs = new String[] {compressionFormat.executable, "-" + level};
}
break;
case DECOMPRESSION:
cliArgs = new String[] {compressionFormat.executable, "-d"};
break;
default:
throw new NullPointerException();
}
ProcessBuilder pb = new ProcessBuilder(cliArgs);
process = pb.start();
processOutput = process.getInputStream();
StreamWriter streamWriter = new StreamWriter(inputStream, process.getOutputStream());
writer = new Thread(streamWriter);
writer.start();
}
@Override
public int read() throws IOException {
return processOutput.read();
}
@Override
public int read(byte[] bytes, int i, int i1) throws IOException {
return processOutput.read(bytes, i, i1);
}
@Override
@SneakyThrows
public void close() {
inputStream.close();
writer.join();
process.waitFor();
processOutput.close();
}
}

View File

@@ -0,0 +1,71 @@
package net.woggioni.jwo.compression;
import lombok.SneakyThrows;
import java.io.OutputStream;
public class CompressionOutputStream extends OutputStream {
private final OutputStream outputStream;
private final Process process;
private final Thread writer;
private final OutputStream processInput;
public CompressionOutputStream(OutputStream os, CompressionFormat compressionFormat) {
this(os, compressionFormat, StreamMode.COMPRESSION);
}
public CompressionOutputStream(OutputStream os, CompressionFormat compressionFormat, Integer level) {
this(os, compressionFormat, StreamMode.COMPRESSION, level);
}
public CompressionOutputStream(OutputStream os, CompressionFormat compressionFormat, StreamMode mode) {
this(os, compressionFormat, mode, null);
}
@SneakyThrows
public CompressionOutputStream(OutputStream os, CompressionFormat compressionFormat, StreamMode mode, Integer level) {
outputStream = os;
String[] cliArgs;
switch(mode) {
case COMPRESSION:
if(level == null) {
cliArgs = new String[] {compressionFormat.executable};
} else {
cliArgs = new String[] {compressionFormat.executable, "-" + level};
}
break;
case DECOMPRESSION:
cliArgs = new String[] {compressionFormat.executable, "-d"};
break;
default:
throw new NullPointerException();
}
ProcessBuilder pb = new ProcessBuilder(cliArgs);
process = pb.start();
processInput = process.getOutputStream();
StreamWriter streamWriter = new StreamWriter(process.getInputStream(), outputStream);
writer = new Thread(streamWriter);
writer.start();
}
@Override
@SneakyThrows
public void write(byte[] buffer, int offset, int size) {
processInput.write(buffer, offset, size);
}
@Override
@SneakyThrows
public void write(int i) {
processInput.write(i);
}
@Override
@SneakyThrows
public void close() {
processInput.close();
process.waitFor();
writer.join();
}
}

View File

@@ -0,0 +1,5 @@
package net.woggioni.jwo.compression;
public enum StreamMode {
COMPRESSION, DECOMPRESSION
}

View File

@@ -0,0 +1,25 @@
package net.woggioni.jwo.compression;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import java.io.InputStream;
import java.io.OutputStream;
@RequiredArgsConstructor
class StreamWriter implements Runnable {
private final InputStream inputStream;
private final OutputStream outputStream;
@SneakyThrows
public void run() {
try {
int c;
while((c = inputStream.read()) >= 0) {
outputStream.write(c);
}
} finally {
outputStream.close();
}
}
}

View File

@@ -0,0 +1,39 @@
package net.woggioni.jwo.hash;
import lombok.SneakyThrows;
import java.io.InputStream;
import java.security.MessageDigest;
public class Hasher {
private Hasher() {}
@SneakyThrows
public static byte[] md5(InputStream is) {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] buffer = new byte[1024];
int read;
while((read = is.read(buffer, 0, buffer.length)) >= 0) {
md.update(buffer, 0, read);
}
return md.digest();
}
public static String md5String(InputStream is) {
return bytesToHex(md5(is));
}
final private static char[] hexArray = "0123456789ABCDEF".toCharArray();
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for(int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
}

View File

@@ -2,7 +2,6 @@ package net.woggioni.jwo.http;
import lombok.AccessLevel;
import lombok.Builder;
import net.woggioni.jwo.http.HttpMethod;
import java.io.InputStream;
import java.net.URL;

View File

@@ -0,0 +1,11 @@
package net.woggioni.jwo.io;
import java.io.OutputStream;
public class NullOutputStream extends OutputStream {
@Override
public void write(int i) {}
@Override
public void write(byte[] bytes) {}
}

View File

@@ -0,0 +1,60 @@
package net.woggioni.jwo.compression;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import net.woggioni.jwo.JWO;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.io.*;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@RequiredArgsConstructor
@RunWith(Parameterized.class)
public class CompressionOutputStreamTest {
@Parameterized.Parameters(name = "Format {0}, level {1}")
public static Iterable<Object[]> data() {
return Arrays.stream(CompressionFormat.values())
.filter(format -> JWO.which(format.executable).isPresent())
.flatMap(v -> IntStream.of(1, 3, 5, 7, 9).mapToObj(l -> new Object[]{v, l}))
.collect(Collectors.toList());
}
private final CompressionFormat compressionFormat;
private final int level;
@Test
@SneakyThrows
public void test() {
MessageDigest inputDigest = MessageDigest.getInstance("MD5");
byte[] compressed;
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try(InputStream is = new BufferedInputStream(new DigestInputStream(
getClass().getResourceAsStream("/cracklib-small"), inputDigest));
CompressionOutputStream compressionOutputStream =
new CompressionOutputStream(new BufferedOutputStream(byteArrayOutputStream), compressionFormat, level)
) {
int read;
byte[] buffer = new byte[1024];
while((read = is.read(buffer, 0, buffer.length)) >= 0) {
compressionOutputStream.write(buffer, 0, read);
}
}
compressed = byteArrayOutputStream.toByteArray();
MessageDigest outputDigest = MessageDigest.getInstance("MD5");
try(InputStream is = new DigestInputStream(new CompressionInputStream(
new ByteArrayInputStream(compressed), compressionFormat, StreamMode.DECOMPRESSION), outputDigest)) {
byte[] buffer = new byte[1024];
while(is.read(buffer, 0, buffer.length) >= 0) {}
}
Assert.assertArrayEquals(inputDigest.digest(), outputDigest.digest());
}
}

File diff suppressed because it is too large Load Diff