forked from openrs2/openrs2
parent
a6dbb29ea0
commit
c21895f052
@ -0,0 +1,98 @@ |
||||
package org.openrs2.compress.bzip2 |
||||
|
||||
import jnr.ffi.Runtime |
||||
import java.io.FilterOutputStream |
||||
import java.io.IOException |
||||
import java.io.OutputStream |
||||
import kotlin.math.min |
||||
|
||||
public class Bzip2OutputStream( |
||||
private val library: LibBzip2, |
||||
out: OutputStream, |
||||
blockSize: Int |
||||
) : FilterOutputStream(out) { |
||||
private val singleByteBuf = ByteArray(1) |
||||
private val runtime = Runtime.getRuntime(library) |
||||
private val stream = LibBzip2.BzStream(runtime) |
||||
private val nextIn = runtime.memoryManager.allocateDirect(BUFFER_SIZE, false) |
||||
private val nextOut = runtime.memoryManager.allocateDirect(BUFFER_SIZE, false) |
||||
private val buf = ByteArray(BUFFER_SIZE) |
||||
private var closed = false |
||||
|
||||
init { |
||||
val result = library.BZ2_bzCompressInit(stream, blockSize, 0, 0) |
||||
if (result != LibBzip2.BZ_OK) { |
||||
throw IOException("bzCompressInit failed: $result") |
||||
} |
||||
|
||||
stream.nextIn.set(nextIn) |
||||
stream.nextOut.set(nextOut) |
||||
} |
||||
|
||||
override fun write(b: Int) { |
||||
singleByteBuf[0] = b.toByte() |
||||
write(singleByteBuf, 0, singleByteBuf.size) |
||||
} |
||||
|
||||
override fun write(b: ByteArray, off: Int, len: Int) { |
||||
var off = off |
||||
var remaining = len |
||||
|
||||
while (remaining > 0) { |
||||
val availIn = min(remaining, BUFFER_SIZE) |
||||
nextIn.put(0, b, off, availIn) |
||||
stream.nextIn.set(nextIn) |
||||
stream.availIn.set(availIn) |
||||
|
||||
stream.nextOut.set(nextOut) |
||||
stream.availOut.set(BUFFER_SIZE) |
||||
|
||||
val result = library.BZ2_bzCompress(stream, LibBzip2.BZ_RUN) |
||||
if (result != LibBzip2.BZ_RUN_OK) { |
||||
throw IOException("bzCompress failed: $result") |
||||
} |
||||
|
||||
val read = (availIn - stream.availIn.get()).toInt() |
||||
off += read |
||||
remaining -= read |
||||
|
||||
val written = (BUFFER_SIZE - stream.availOut.get()).toInt() |
||||
nextOut.get(0, buf, 0, written) |
||||
out.write(buf, 0, written) |
||||
} |
||||
} |
||||
|
||||
override fun close() { |
||||
if (closed) { |
||||
return |
||||
} |
||||
|
||||
closed = true |
||||
|
||||
try { |
||||
do { |
||||
stream.nextOut.set(nextOut) |
||||
stream.availOut.set(BUFFER_SIZE) |
||||
|
||||
val streamEnd = when (val result = library.BZ2_bzCompress(stream, LibBzip2.BZ_FINISH)) { |
||||
LibBzip2.BZ_STREAM_END -> true |
||||
LibBzip2.BZ_FINISH_OK -> false |
||||
else -> throw IOException("bzCompress failed: $result") |
||||
} |
||||
|
||||
val written = (BUFFER_SIZE - stream.availOut.get()).toInt() |
||||
nextOut.get(0, buf, 0, written) |
||||
out.write(buf, 0, written) |
||||
} while (!streamEnd) |
||||
} finally { |
||||
val result = library.BZ2_bzCompressEnd(stream) |
||||
if (result != LibBzip2.BZ_OK) { |
||||
throw IOException("bzCompressEnd failed: $result") |
||||
} |
||||
} |
||||
} |
||||
|
||||
private companion object { |
||||
private const val BUFFER_SIZE = 4096 |
||||
} |
||||
} |
@ -0,0 +1,64 @@ |
||||
package org.openrs2.compress.bzip2 |
||||
|
||||
import jnr.ffi.LibraryLoader |
||||
import jnr.ffi.LibraryOption |
||||
import jnr.ffi.Runtime |
||||
import jnr.ffi.Struct |
||||
import jnr.ffi.annotations.Direct |
||||
|
||||
public interface LibBzip2 { |
||||
public class BzStream(runtime: Runtime) : Struct(runtime) { |
||||
public val nextIn: Pointer = Pointer() |
||||
public val availIn: Unsigned32 = Unsigned32() |
||||
public val totalInLo32: Unsigned32 = Unsigned32() |
||||
public val totalInHi32: Unsigned32 = Unsigned32() |
||||
|
||||
public val nextOut: Pointer = Pointer() |
||||
public val availOut: Unsigned32 = Unsigned32() |
||||
public val totalOutLo32: Unsigned32 = Unsigned32() |
||||
public val totalOutHi32: Unsigned32 = Unsigned32() |
||||
|
||||
public val state: Pointer = Pointer() |
||||
|
||||
public val alloc: Pointer = Pointer() |
||||
public val free: Pointer = Pointer() |
||||
public val opaque: Pointer = Pointer() |
||||
} |
||||
|
||||
public fun BZ2_bzCompressInit(@Direct stream: BzStream, blockSize100k: Int, verbosity: Int, workFactor: Int): Int |
||||
public fun BZ2_bzCompress(stream: BzStream, action: Int): Int |
||||
public fun BZ2_bzCompressEnd(stream: BzStream): Int |
||||
|
||||
public fun BZ2_bzDecompressInit(@Direct stream: BzStream, blockSize100k: Int, verbosity: Int, small: Int): Int |
||||
public fun BZ2_bzDecompress(stream: BzStream): Int |
||||
public fun BZ2_bzDecompressEnd(stream: BzStream): Int |
||||
|
||||
public companion object { |
||||
public const val BZ_RUN: Int = 0 |
||||
public const val BZ_FLUSH: Int = 1 |
||||
public const val BZ_FINISH: Int = 2 |
||||
|
||||
public const val BZ_OK: Int = 0 |
||||
public const val BZ_RUN_OK: Int = 1 |
||||
public const val BZ_FLUSH_OK: Int = 2 |
||||
public const val BZ_FINISH_OK: Int = 3 |
||||
public const val BZ_STREAM_END: Int = 4 |
||||
public const val BZ_SEQUENCE_ERROR: Int = -1 |
||||
public const val BZ_PARAM_ERROR: Int = -2 |
||||
public const val BZ_MEM_ERROR: Int = -3 |
||||
public const val BZ_DATA_ERROR: Int = -4 |
||||
public const val BZ_DATA_ERROR_MAGIC: Int = -5 |
||||
public const val BZ_IO_ERROR: Int = -6 |
||||
public const val BZ_UNEXPECTED_EOF: Int = -7 |
||||
public const val BZ_OUTBUFF_FULL: Int = -8 |
||||
public const val BZ_CONFIG_ERROR: Int = -9 |
||||
|
||||
public fun load(): LibBzip2 { |
||||
return LibraryLoader.loadLibrary( |
||||
LibBzip2::class.java, mapOf( |
||||
LibraryOption.LoadNow to true, |
||||
), "bz2" |
||||
) |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue