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.*;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import java.util.stream.StreamSupport;
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
@@ -351,4 +352,12 @@ public class JWO {
|
|||||||
"Unknown value '%s' for enum %s", value, cls.getName());
|
"Unknown value '%s' for enum %s", value, cls.getName());
|
||||||
return result;
|
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.AccessLevel;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import net.woggioni.jwo.http.HttpMethod;
|
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.URL;
|
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