added cli tool
This commit is contained in:
@@ -5,10 +5,18 @@ plugins {
|
|||||||
group = "net.woggioni"
|
group = "net.woggioni"
|
||||||
version = "0.1"
|
version = "0.1"
|
||||||
|
|
||||||
repositories {
|
java {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
|
allprojects {
|
||||||
|
|
||||||
|
repositories {
|
||||||
mavenLocal()
|
mavenLocal()
|
||||||
jcenter()
|
jcenter()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun property(name : String) = project.property(name).toString()
|
fun property(name : String) = project.property(name).toString()
|
||||||
|
49
cli/build.gradle.kts
Normal file
49
cli/build.gradle.kts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
plugins {
|
||||||
|
id("org.jetbrains.kotlin.jvm") version "1.3.70"
|
||||||
|
application
|
||||||
|
}
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||||
|
|
||||||
|
|
||||||
|
group = "net.woggioni"
|
||||||
|
version = "0.1"
|
||||||
|
|
||||||
|
fun property(name : String) = project.property(name).toString()
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
// Align versions of all Kotlin components
|
||||||
|
implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
|
||||||
|
|
||||||
|
// Use the Kotlin JDK 8 standard library.
|
||||||
|
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||||
|
|
||||||
|
implementation("org.apache.logging.log4j", "log4j-slf4j-impl", property("log4j.version"))
|
||||||
|
implementation("org.slf4j", "slf4j-api", property("slf4j.version"))
|
||||||
|
implementation("com.beust", "jcommander", property("jcommander.version"))
|
||||||
|
implementation(project(":"))
|
||||||
|
|
||||||
|
testRuntimeOnly("org.junit.jupiter", "junit-jupiter-engine", property("junit.version"))
|
||||||
|
testImplementation("org.junit.jupiter","junit-jupiter-api", property("junit.version"))
|
||||||
|
testImplementation("org.junit.jupiter","junit-jupiter-params", property("junit.version"))
|
||||||
|
}
|
||||||
|
|
||||||
|
application {
|
||||||
|
mainClassName = "net.woggioni.jzstd.Cli"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
tasks.withType<Test> {
|
||||||
|
useJUnitPlatform {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
tasks.withType<KotlinCompile>().configureEach {
|
||||||
|
kotlinOptions {
|
||||||
|
languageVersion = "1.3"
|
||||||
|
apiVersion = "1.3"
|
||||||
|
jvmTarget = "1.8"
|
||||||
|
javaParameters = true // Useful for reflection.
|
||||||
|
}
|
||||||
|
}
|
126
cli/src/main/kotlin/net/woggioni/jzstd/Cli.kt
Normal file
126
cli/src/main/kotlin/net/woggioni/jzstd/Cli.kt
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
package net.woggioni.jzstd
|
||||||
|
|
||||||
|
import com.beust.jcommander.JCommander
|
||||||
|
import com.beust.jcommander.Parameter
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import java.io.BufferedInputStream
|
||||||
|
import java.io.BufferedOutputStream
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.io.OutputStream
|
||||||
|
import java.nio.file.Files
|
||||||
|
import java.nio.file.Paths
|
||||||
|
|
||||||
|
object Cli {
|
||||||
|
|
||||||
|
private data class Params(
|
||||||
|
@Parameter
|
||||||
|
var parameters: List<String> = ArrayList(),
|
||||||
|
|
||||||
|
@Parameter(names = arrayOf("-c", "--stdout"), description = "Output to stdout")
|
||||||
|
var console: Boolean = false,
|
||||||
|
|
||||||
|
@Parameter(names = arrayOf("-d", "--decompress"), description = "Decompress input")
|
||||||
|
var decompress: Boolean = false,
|
||||||
|
|
||||||
|
@Parameter(names = arrayOf("-z", "--compress"), description = "Compress input")
|
||||||
|
var compress: Boolean = false,
|
||||||
|
|
||||||
|
@Parameter(names = arrayOf("-k", "--keep"), description = "Keep input file")
|
||||||
|
var keep: Boolean = false,
|
||||||
|
|
||||||
|
@Parameter(names = arrayOf("-f", "--overwrite"), description = "Overwrite destination")
|
||||||
|
var overwrite: Boolean = false,
|
||||||
|
|
||||||
|
@Parameter(names = arrayOf("-l", "--level"), description = "Set compression level")
|
||||||
|
var level: Int = 3,
|
||||||
|
|
||||||
|
@Parameter(names = arrayOf("-i", "--input"), description = "Set input file, defaults to stdin otherwise")
|
||||||
|
var input: String? = null,
|
||||||
|
|
||||||
|
@Parameter(names = arrayOf("-o", "--output"), description = "Set output file")
|
||||||
|
var output: String? = null,
|
||||||
|
|
||||||
|
@Parameter(names = arrayOf("-h", "--help"), help = true)
|
||||||
|
var help: Boolean = false
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
val log = LoggerFactory.getLogger(Cli::class.java)
|
||||||
|
|
||||||
|
private fun fileNameWithoutExtension(fileName: String) = fileName.let {
|
||||||
|
when (val dotIndex = it.lastIndexOf('.')) {
|
||||||
|
-1 -> it
|
||||||
|
else -> it.substring(0, dotIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun main(argv: Array<String>) {
|
||||||
|
val params = Params()
|
||||||
|
val jc = JCommander.newBuilder()
|
||||||
|
.addObject(params)
|
||||||
|
.build()
|
||||||
|
jc.parse(*argv)
|
||||||
|
if (params.help) {
|
||||||
|
jc.usage()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!params.compress && !params.decompress) params.compress = true
|
||||||
|
try {
|
||||||
|
val input: InputStream = (params.input
|
||||||
|
?.let { Paths.get(it) }
|
||||||
|
?.let { Files.newInputStream(it) }
|
||||||
|
?: System.`in`)
|
||||||
|
.let {
|
||||||
|
if (params.decompress) ZstdInputStream.from(it)
|
||||||
|
else it
|
||||||
|
}
|
||||||
|
input.use { inputStream ->
|
||||||
|
val output: OutputStream = (params.output.let {
|
||||||
|
when (it) {
|
||||||
|
null -> if (params.input == null || params.console) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
when {
|
||||||
|
params.compress && params.decompress ->
|
||||||
|
throw IllegalArgumentException("Only one between --compress or --decompress must be selected")
|
||||||
|
params.compress -> {
|
||||||
|
Paths.get(params.input + ".zst")
|
||||||
|
}
|
||||||
|
params.decompress -> {
|
||||||
|
val inputFile = Paths.get(params.input)
|
||||||
|
val inputDir = inputFile.parent ?: Paths.get(".")
|
||||||
|
inputDir.resolve(fileNameWithoutExtension(inputFile.fileName.toString()))
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
throw NotImplementedError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> Paths.get(it)
|
||||||
|
}
|
||||||
|
}?.also { destination ->
|
||||||
|
if (!params.overwrite && Files.exists(destination)) {
|
||||||
|
throw IllegalStateException("Destination file $destination already exists")
|
||||||
|
}
|
||||||
|
}?.let {
|
||||||
|
Files.newOutputStream(it)
|
||||||
|
} ?: System.out).let {
|
||||||
|
if (params.compress) ZstdOutputStream.from(it, params.level)
|
||||||
|
else it
|
||||||
|
}
|
||||||
|
output.use { outputStream ->
|
||||||
|
val buffer = ByteArray(0x10000)
|
||||||
|
while (true) {
|
||||||
|
val read = inputStream.read(buffer, 0, buffer.size)
|
||||||
|
if (read < 0) break
|
||||||
|
outputStream.write(buffer, 0, read)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (params.input != null && params.output == null && !params.console && !params.keep) Files.delete(Paths.get(params.input))
|
||||||
|
} catch (t: Throwable) {
|
||||||
|
log.error(t.message, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
cli/src/main/resources/log4j2.xml
Normal file
17
cli/src/main/resources/log4j2.xml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Configuration status="WARN">
|
||||||
|
<Appenders>
|
||||||
|
<Console name="Console" target="SYSTEM_ERR">
|
||||||
|
<PatternLayout pattern="%d{HH:mm:ss,SSS} %highlight{[%p]} (%t) %c: %m%n"/>
|
||||||
|
<Filters>
|
||||||
|
<ThresholdFilter level="INFO" onMatch="ACCEPT" />
|
||||||
|
</Filters>
|
||||||
|
</Console>
|
||||||
|
</Appenders>
|
||||||
|
|
||||||
|
<Loggers>
|
||||||
|
<Root level="ALL">
|
||||||
|
<AppenderRef ref="Console"/>
|
||||||
|
</Root>
|
||||||
|
</Loggers>
|
||||||
|
</Configuration>
|
22
cli/src/test/kotlin/InsaneTest.kt
Normal file
22
cli/src/test/kotlin/InsaneTest.kt
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import net.woggioni.jzstd.ZstdOutputStream
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import java.nio.file.Files
|
||||||
|
import java.nio.file.Paths
|
||||||
|
|
||||||
|
class InsaneTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun test2() {
|
||||||
|
val inputFile = Paths.get("/tmp/Richard_Dawkins_The_Selfish_Gene.pdf")
|
||||||
|
Files.newInputStream(inputFile).use { `is` ->
|
||||||
|
ZstdOutputStream.from(Files.newOutputStream(Paths.get("/tmp/Richard_Dawkins_The_Selfish_Gene.pdf.zst"))).use { os ->
|
||||||
|
val buffer = ByteArray(0x10000)
|
||||||
|
while (true) {
|
||||||
|
val read = `is`.read(buffer)
|
||||||
|
if (read < 0) break
|
||||||
|
os.write(buffer, 0, read)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -2,3 +2,6 @@ junit.version=5.6.2
|
|||||||
lombok.version=1.18.12
|
lombok.version=1.18.12
|
||||||
jna.version=5.5.0
|
jna.version=5.5.0
|
||||||
kotlin.version=1.3.70
|
kotlin.version=1.3.70
|
||||||
|
jcommander.version=1.78
|
||||||
|
slf4j.version=1.7.30
|
||||||
|
log4j.version=2.13.2
|
@@ -1 +1,3 @@
|
|||||||
rootProject.name = "jzstd"
|
rootProject.name = "jzstd"
|
||||||
|
|
||||||
|
include("cli")
|
||||||
|
@@ -87,7 +87,7 @@ public class ZstdOutputStream extends OutputStream {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(byte[] arr, int off, int len) {
|
public void write(byte[] arr, int off, int len) {
|
||||||
var written = 0;
|
int written = 0;
|
||||||
while (written < len - off) {
|
while (written < len - off) {
|
||||||
int writeSize = Math.min(len, input.src.capacity() - input.src.position());
|
int writeSize = Math.min(len, input.src.capacity() - input.src.position());
|
||||||
input.src.put(arr, off, writeSize);
|
input.src.put(arr, off, writeSize);
|
||||||
|
@@ -2,6 +2,7 @@ package net.woggioni.jzstd;
|
|||||||
|
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.Arguments;
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
@@ -10,9 +11,9 @@ import org.junit.jupiter.params.provider.ArgumentsSource;
|
|||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.security.DigestInputStream;
|
import java.security.DigestInputStream;
|
||||||
import java.security.DigestOutputStream;
|
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@@ -38,9 +39,9 @@ public class BasicTest {
|
|||||||
MessageDigest md5 = MessageDigest.getInstance("MD5");
|
MessageDigest md5 = MessageDigest.getInstance("MD5");
|
||||||
|
|
||||||
ByteArrayInputStream compressedStream;
|
ByteArrayInputStream compressedStream;
|
||||||
try (InputStream is = new BufferedInputStream(testStream)) {
|
try (InputStream is = new DigestInputStream(new BufferedInputStream(testStream), md5)) {
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
try (OutputStream os = new DigestOutputStream(ZstdOutputStream.from(baos), md5)) {
|
try (OutputStream os = ZstdOutputStream.from(baos)) {
|
||||||
byte[] buffer = new byte[0x1000];
|
byte[] buffer = new byte[0x1000];
|
||||||
while (true) {
|
while (true) {
|
||||||
int read = is.read(buffer);
|
int read = is.read(buffer);
|
||||||
@@ -54,12 +55,29 @@ public class BasicTest {
|
|||||||
md5.reset();
|
md5.reset();
|
||||||
try (InputStream is = new DigestInputStream(
|
try (InputStream is = new DigestInputStream(
|
||||||
ZstdInputStream.from(compressedStream), md5)) {
|
ZstdInputStream.from(compressedStream), md5)) {
|
||||||
byte [] buffer = new byte[0x10000];
|
byte [] buffer = new byte[0x100000];
|
||||||
while(true) {
|
while(true) {
|
||||||
int read = is.read(buffer);
|
int read = is.read(buffer);
|
||||||
if(read < 0) break;
|
if(read < 0) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Assertions.assertArrayEquals(originalDigest, md5.digest());
|
byte[] roundTripDigest = md5.digest();
|
||||||
|
Assertions.assertArrayEquals(originalDigest, roundTripDigest);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
@Test
|
||||||
|
public void test2() {
|
||||||
|
Path inputFile = Paths.get("/tmp/Richard_Dawkins_The_Selfish_Gene.pdf");
|
||||||
|
try(InputStream is = Files.newInputStream(inputFile)) {
|
||||||
|
try(OutputStream os = ZstdOutputStream.from(Files.newOutputStream(Paths.get("/tmp/Richard_Dawkins_The_Selfish_Gene.pdf.zst")))) {
|
||||||
|
byte[] buffer = new byte[0x10000];
|
||||||
|
while(true) {
|
||||||
|
int read = is.read(buffer);
|
||||||
|
if(read < 0) break;
|
||||||
|
os.write(buffer, 0, read);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user