Fix set compresion, added nbt and snbt(wip) lib

This commit is contained in:
2026-09-05 16:35:55 +08:00
parent 60324028ee
commit 5e6e0ca5b9
56 files changed
+1064 -544

No files matched your search

@@ -11,4 +11,8 @@ public expect fun ByteArray.zlibDecompress(): ByteArray
public expect fun ByteArray.zlibDecompress(expectedSize: Int): ByteArray
public expect fun ByteArray.zlibCompress(): ByteArray
public expect fun ByteArray.zlibCompress(): ByteArray
public expect fun ByteArray.gzipCompress(): ByteArray
public expect fun ByteArray.gzipDecompress(): ByteArray
@@ -10,6 +10,7 @@ import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.util.zip.Deflater
import java.util.zip.GZIPInputStream
import java.util.zip.GZIPOutputStream
import java.util.zip.Inflater
public actual fun ByteArray.zlibDecompress(): ByteArray {
@@ -29,28 +30,18 @@ public actual fun ByteArray.zlibDecompress(): ByteArray {
}
}
private fun ByteArray.gzipDecompress(): ByteArray {
if (isEmpty()) return byteArrayOf()
ByteArrayInputStream(this).use { bais ->
GZIPInputStream(bais).use { gzis ->
val outputStream = ByteArrayOutputStream(this.size * 2)
val buffer = ByteArray(1024)
var len: Int
while (gzis.read(buffer).also { len = it } != -1) {
outputStream.write(buffer, 0, len)
}
return outputStream.toByteArray()
}
}
}
public actual fun ByteArray.zlibDecompress(expectedSize: Int): ByteArray {
val inflater = Inflater()
inflater.setInput(this)
val result = ByteArray(expectedSize)
var totalRead = 0
try {
val resultLength = inflater.inflate(result)
check(resultLength == expectedSize) { "Decompression failed: expected $expectedSize bytes, but got $resultLength" }
while (!inflater.finished() && totalRead < expectedSize) {
val read = inflater.inflate(result, totalRead, expectedSize - totalRead)
if (read == 0) break
totalRead += read
}
check(totalRead == expectedSize) { "Decompression failed: expected $expectedSize bytes, but got $totalRead" }
return result
} finally {
inflater.end()
@@ -61,8 +52,22 @@ public actual fun ByteArray.zlibCompress(): ByteArray {
val deflater = Deflater()
deflater.setInput(this)
deflater.finish()
val output = ByteArray(this.size + 64)
val compressedSize = deflater.deflate(output)
val bos = ByteArrayOutputStream(this.size)
val buffer = ByteArray(1024)
while (!deflater.finished()) {
val count = deflater.deflate(buffer)
if (count > 0) bos.write(buffer, 0, count)
}
deflater.end()
return output.copyOf(compressedSize)
return bos.toByteArray()
}
public actual fun ByteArray.gzipCompress(): ByteArray {
val bos = ByteArrayOutputStream()
GZIPOutputStream(bos).use { it.write(this) }
return bos.toByteArray()
}
public actual fun ByteArray.gzipDecompress(): ByteArray {
return GZIPInputStream(ByteArrayInputStream(this)).use { it.readBytes() }
}
@@ -1,104 +1,131 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/4
* Copyright © 2025-2026 RTAkland
* Open Source Under Apache-2.0 License
* https://www.apache.org/licenses/LICENSE-2.0
*/
@file:OptIn(ExperimentalForeignApi::class, UnsafeNumber::class)
@file:OptIn(ExperimentalForeignApi::class)
package cn.rtast.libmc.common
import kotlinx.cinterop.*
import platform.posix.u_longVar
import platform.zlib.*
private const val ENABLE_ZLIB_GZIP_HEADER = 15 + 32
private const val MAX_WBITS = 15
private const val DEFAULT_BUFFER_SIZE = 4096
public actual fun ByteArray.zlibDecompress(): ByteArray {
if (isEmpty()) return ByteArray(0)
return memScoped {
val stream = alloc<z_stream>()
stream.zalloc = null
stream.zfree = null
stream.opaque = null
private inline fun ByteArray.processDecompress(initStream: (CPointer<z_stream>) -> Unit): ByteArray = memScoped {
val stream = alloc<z_stream>()
stream.zalloc = null
stream.zfree = null
stream.opaque = null
initStream(stream.ptr)
stream.next_in = this@processDecompress.refTo(0).getPointer(this).reinterpret()
stream.avail_in = this@processDecompress.size.toUInt()
val output = mutableListOf<Byte>()
val tempBuffer = ByteArray(DEFAULT_BUFFER_SIZE)
val initResult = inflateInit2_(
stream.ptr,
ENABLE_ZLIB_GZIP_HEADER,
ZLIB_VERSION,
sizeOf<z_stream>().toInt()
)
check(initResult == Z_OK) { "inflateInit2_ failed with code: $initResult" }
try {
do {
stream.next_out = tempBuffer.refTo(0).getPointer(this).reinterpret()
stream.avail_out = DEFAULT_BUFFER_SIZE.toUInt()
val result = inflate(stream.ptr, Z_NO_FLUSH)
check(result == Z_OK || result == Z_STREAM_END) { "inflate error: $result" }
val bytesDecompressed = DEFAULT_BUFFER_SIZE - stream.avail_out.toInt()
output.addAll(tempBuffer.take(bytesDecompressed))
val inputPinned = this@zlibDecompress.pin()
try {
stream.next_in = inputPinned.addressOf(0).reinterpret()
stream.avail_in = this@zlibDecompress.size.toUInt()
val bufferSize = 4096
val tempBuffer = ByteArray(bufferSize)
val tempPinned = tempBuffer.pin()
val output = ArrayList<Byte>(this@zlibDecompress.size * 3)
try {
var result: Int
do {
stream.next_out = tempPinned.addressOf(0).reinterpret()
stream.avail_out = bufferSize.toUInt()
result = inflate(stream.ptr, Z_NO_FLUSH)
check(result == Z_OK || result == Z_STREAM_END) { "inflate error: $result" }
val bytesDecompressed = bufferSize - stream.avail_out.toInt()
for (i in 0 until bytesDecompressed) {
output.add(tempBuffer[i])
}
if (result == Z_STREAM_END) break
} while (stream.avail_in > 0u || stream.avail_out == 0u)
} finally {
tempPinned.unpin()
}
inflateEnd(stream.ptr)
return@memScoped output.toByteArray()
} finally {
inputPinned.unpin()
}
if (result == Z_STREAM_END) break
} while (stream.avail_out == 0u)
} finally {
inflateEnd(stream.ptr)
}
return output.toByteArray()
}
public actual fun ByteArray.zlibDecompress(expectedSize: Int): ByteArray {
private inline fun ByteArray.processCompress(initStream: (CPointer<z_stream>) -> Unit): ByteArray = memScoped {
val stream = alloc<z_stream>()
stream.zalloc = null
stream.zfree = null
stream.opaque = null
initStream(stream.ptr)
stream.next_in = this@processCompress.refTo(0).getPointer(this).reinterpret()
stream.avail_in = this@processCompress.size.toUInt()
val output = mutableListOf<Byte>()
val tempBuffer = ByteArray(DEFAULT_BUFFER_SIZE)
try {
do {
stream.next_out = tempBuffer.refTo(0).getPointer(this).reinterpret()
stream.avail_out = DEFAULT_BUFFER_SIZE.toUInt()
val result = deflate(stream.ptr, Z_FINISH)
check(result == Z_OK || result == Z_STREAM_END) { "deflate error: $result" }
val have = DEFAULT_BUFFER_SIZE - stream.avail_out.toInt()
output.addAll(tempBuffer.take(have))
} while (stream.avail_out == 0u)
} finally {
deflateEnd(stream.ptr)
}
return output.toByteArray()
}
public actual fun ByteArray.zlibDecompress(expectedSize: Int): ByteArray = memScoped {
val stream = alloc<z_stream>()
stream.zalloc = null
stream.zfree = null
stream.opaque = null
check(inflateInit_(stream.ptr, ZLIB_VERSION, sizeOf<z_stream>().toInt()) == Z_OK) { "inflateInit_ failed" }
val result = ByteArray(expectedSize)
if (this.isEmpty()) return result
memScoped {
val destLen = alloc<u_longVar>()
destLen.value = expectedSize.toUInt()
val res = uncompress(
result.refTo(0).getPointer(this).reinterpret(),
destLen.ptr,
this@zlibDecompress.refTo(0).getPointer(this).reinterpret(),
this@zlibDecompress.size.toUInt()
)
check(res == Z_OK) { "zlib uncompress failed with error code: $res" }
try {
stream.next_in = this@zlibDecompress.refTo(0).getPointer(this).reinterpret()
stream.avail_in = this@zlibDecompress.size.toUInt()
stream.next_out = result.refTo(0).getPointer(this).reinterpret()
stream.avail_out = expectedSize.toUInt()
var totalRead = 0
while (stream.avail_in > 0u && totalRead < expectedSize) {
val status = inflate(stream.ptr, Z_NO_FLUSH)
check(status == Z_STREAM_END || status == Z_OK) { "inflate failed with status: $status" }
val decompressedThisTurn = expectedSize - totalRead - stream.avail_out.toInt()
totalRead += decompressedThisTurn
if (status == Z_STREAM_END) break
}
check(totalRead == expectedSize) {
"Decompression failed: expected $expectedSize bytes, but got $totalRead"
}
return result
} finally {
inflateEnd(stream.ptr)
}
return result
}
public actual fun ByteArray.zlibCompress(): ByteArray {
if (this.isEmpty()) return byteArrayOf()
val maxCompressedLen = compressBound(this.size.toUInt()).toInt()
val output = ByteArray(maxCompressedLen)
memScoped {
val destLen = alloc<u_longVar>()
destLen.value = maxCompressedLen.toUInt()
val res = compress(
output.refTo(0).getPointer(this).reinterpret(),
destLen.ptr,
this@zlibCompress.refTo(0).getPointer(this).reinterpret(),
this@zlibCompress.size.toUInt()
)
check(res == Z_OK) { "zlib compress failed with error code: $res" }
return output.copyOf(destLen.value.toInt())
}
public actual fun ByteArray.gzipCompress(): ByteArray = processCompress { ptr ->
check(
deflateInit2_(
ptr, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
16 + MAX_WBITS, 8, Z_DEFAULT_STRATEGY, ZLIB_VERSION, sizeOf<z_stream>().toInt()
) == Z_OK
) { "deflateInit2_ failed" }
}
public actual fun ByteArray.gzipDecompress(): ByteArray = processDecompress { ptr ->
check(
inflateInit2_(
ptr, 16 + MAX_WBITS, ZLIB_VERSION, sizeOf<z_stream>().toInt()
) == Z_OK
) { "inflateInit2_ failed" }
}
public actual fun ByteArray.zlibCompress(): ByteArray = processCompress { ptr ->
check(
deflateInit_(
ptr, Z_DEFAULT_COMPRESSION, ZLIB_VERSION, sizeOf<z_stream>().toInt()
) == Z_OK
) { "deflateInit_ failed" }
}
public actual fun ByteArray.zlibDecompress(): ByteArray = processDecompress { ptr ->
check(
inflateInit_(
ptr, ZLIB_VERSION, sizeOf<z_stream>().toInt()
) == Z_OK
) { "inflateInit_ failed" }
}