added net.woggioni.jwo.compression.CompressionOutputStream and net.woggioni.jwo.compression.CompressionInputStream
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -0,0 +1,5 @@
|
||||
package net.woggioni.jwo.compression;
|
||||
|
||||
public enum StreamMode {
|
||||
COMPRESSION, DECOMPRESSION
|
||||
}
|
25
src/main/java/net/woggioni/jwo/compression/StreamWriter.java
Normal file
25
src/main/java/net/woggioni/jwo/compression/StreamWriter.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
39
src/main/java/net/woggioni/jwo/hash/Hasher.java
Normal file
39
src/main/java/net/woggioni/jwo/hash/Hasher.java
Normal 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);
|
||||
}
|
||||
}
|
@@ -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;
|
||||
|
11
src/main/java/net/woggioni/jwo/io/NullOutputStream.java
Normal file
11
src/main/java/net/woggioni/jwo/io/NullOutputStream.java
Normal 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) {}
|
||||
}
|
@@ -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());
|
||||
}
|
||||
}
|
54763
src/test/resources/cracklib-small
Normal file
54763
src/test/resources/cracklib-small
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user