added cli tool
This commit is contained in:
@@ -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
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -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
|
@@ -1 +1,3 @@
|
||||
rootProject.name = "jzstd"
|
||||
|
||||
include("cli")
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user