added cli tool

This commit is contained in:
2020-04-27 19:41:06 +01:00
parent 0479cf0560
commit 9f48d6ee3a
9 changed files with 256 additions and 11 deletions

View File

@@ -5,10 +5,18 @@ plugins {
group = "net.woggioni"
version = "0.1"
repositories {
mavenLocal()
jcenter()
mavenCentral()
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
allprojects {
repositories {
mavenLocal()
jcenter()
mavenCentral()
}
}
fun property(name : String) = project.property(name).toString()

49
cli/build.gradle.kts Normal file
View 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.
}
}

View 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)
}
}
}

View 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>

View 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)
}
}
}
}
}

View File

@@ -1,4 +1,7 @@
junit.version=5.6.2
lombok.version=1.18.12
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

View File

@@ -1 +1,3 @@
rootProject.name = "jzstd"
include("cli")

View File

@@ -87,7 +87,7 @@ public class ZstdOutputStream extends OutputStream {
@Override
public void write(byte[] arr, int off, int len) {
var written = 0;
int written = 0;
while (written < len - off) {
int writeSize = Math.min(len, input.src.capacity() - input.src.position());
input.src.put(arr, off, writeSize);

View File

@@ -2,6 +2,7 @@ package net.woggioni.jzstd;
import lombok.SneakyThrows;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
@@ -10,9 +11,9 @@ import org.junit.jupiter.params.provider.ArgumentsSource;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.DigestInputStream;
import java.security.DigestOutputStream;
import java.security.MessageDigest;
import java.util.stream.Stream;
@@ -38,9 +39,9 @@ public class BasicTest {
MessageDigest md5 = MessageDigest.getInstance("MD5");
ByteArrayInputStream compressedStream;
try (InputStream is = new BufferedInputStream(testStream)) {
try (InputStream is = new DigestInputStream(new BufferedInputStream(testStream), md5)) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (OutputStream os = new DigestOutputStream(ZstdOutputStream.from(baos), md5)) {
try (OutputStream os = ZstdOutputStream.from(baos)) {
byte[] buffer = new byte[0x1000];
while (true) {
int read = is.read(buffer);
@@ -54,12 +55,29 @@ public class BasicTest {
md5.reset();
try (InputStream is = new DigestInputStream(
ZstdInputStream.from(compressedStream), md5)) {
byte [] buffer = new byte[0x10000];
byte [] buffer = new byte[0x100000];
while(true) {
int read = is.read(buffer);
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);
}
}
}
}
}