All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.korlibs.io.compression.deflate.GZIP.kt Maven / Gradle / Ivy

The newest version!
package korlibs.io.compression.deflate

import korlibs.compression.deflate.*
import korlibs.io.compression.CompressionContext
import korlibs.io.compression.CompressionMethod
import korlibs.io.lang.invalidOp
import korlibs.io.stream.*
import korlibs.io.util.checksum.CRC32

open class GZIP(deflater: () -> IDeflater) : GZIPBase(true, deflater) {
	companion object : GZIP({ Deflate }) {
        override fun toString(): String = "GZIP"
    }
}
open class GZIPNoCrc(deflater: () -> IDeflater) : GZIPBase(false, deflater) {
	companion object : GZIPNoCrc({ Deflate }) {
        override fun toString(): String = "GZIPNoCrc"
    }
}

open class GZIPBase(val checkCrc: Boolean, val deflater: () -> IDeflater) : CompressionMethod {
    override val name: String get() = "GZIP"

    override fun toString(): String = "GZIPBase($checkCrc, ${deflater})"

	@OptIn(ExperimentalStdlibApi::class)
	override suspend fun uncompress(i: AsyncInputStream, out: AsyncOutputStream) {
		val r = BitReader(i)
		r.prepareBigChunkIfRequired()
        val h0 = r.su8()
        val h1 = r.su8()
        if (h0 != 31 || h1 != 139) error("Not a GZIP file (h0=${h0.toByte().toHexString()}, h1=${h1.toByte().toHexString()})")
		val method = r.su8()
		if (method != 8) error("Just supported deflate in GZIP (method=$method)")
		val ftext = r.sreadBit()
		val fhcrc = r.sreadBit()
		val fextra = r.sreadBit()
		val fname = r.sreadBit()
		val fcomment = r.sreadBit()
		val reserved = r.readBits(3)
		val mtime = r.su32LE()
		val xfl = r.su8()
		val os = r.su8()
		val extra = if (fextra) r.abytes(r.su16LE()) else byteArrayOf()
		val name = if (fname) r.strz() else null
		val comment = if (fcomment) r.strz() else null
		val crc16 = if (fhcrc) r.su16LE() else 0
		var ccrc32 = CRC32.initialValue
		var csize = 0
		(deflater() as IDeflaterInternal).uncompress(r.toDeflater(), object : AsyncOutputStream by out {
			override suspend fun write(buffer: ByteArray, offset: Int, len: Int) {
				if (len > 0) {
					//val oldCrc32 = ccrc32
					ccrc32 = CRC32.update(ccrc32, buffer, offset, len)
					csize += len

					//val chunk = buffer.sliceArray(offset until offset + len)
					//println("DECOMPRESS:" + chunk.toList() + " sum=${chunk.toList().sum()} oldCrc32=$oldCrc32, crc32=$ccrc32 [offset=$offset, len=$len]")
					out.write(buffer, offset, len)
				}
			}
		}.toDeflater())
		r.prepareBigChunkIfRequired()
		val crc32 = r.su32LE()
		val size = r.su32LE()
		//println("COMPRESS: crc32=$crc32, size=$size, ccrc32=$ccrc32, csize=$csize")
		if (checkCrc && (csize != size || ccrc32 != crc32)) {
			invalidOp("Size doesn't match SIZE(${csize.toHexString()} != ${size.toHexString()}) || CRC32(${ccrc32.toHexString()} != ${crc32.toHexString()})")
		}
	}

	override suspend fun compress(
		i: AsyncInputStream,
		o: AsyncOutputStream,
		context: CompressionContext
	) {
		val i = BitReader(i)
		o.write8(31) // MAGIC[0]
		o.write8(139) // MAGIC[1]
		o.write8(8) // METHOD=8 (deflate)
		o.write8(0) // Presence bits
		o.write32LE(0) // Time
		o.write8(0) // xfl
		o.write8(0) // os

		var size = 0
		var crc32 = CRC32.initialValue
		deflater().compress(BitReader.forInput(object : AsyncInputStreamWithLength by i {
			override suspend fun read(buffer: ByteArray, offset: Int, len: Int): Int {
				val read = i.read(buffer, offset, len)
				if (read > 0) {
					//val oldCrc32 = crc32
					crc32 = CRC32.update(crc32, buffer, offset, read)
					//val chunk = buffer.sliceArray(offset until offset + read)
					//println("COMPRESS:" + chunk.toList() + " sum=${chunk.toList().sum()} oldCrc32=$oldCrc32, crc32=$crc32 [offset=$offset, read=$read]")
					size += read
				}
				return read
			}
		}), o, context)
		o.write32LE(crc32)
		o.write32LE(size)
		//println("COMPRESS: crc32=$crc32, size=$size")
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy