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

commonMain.korlibs.io.stream.FastByteArrayInputStream.kt Maven / Gradle / Ivy

@file:Suppress("PackageDirectoryMismatch")

package korlibs.io.stream

import korlibs.io.lang.*
import korlibs.io.stream.internal.*
import korlibs.io.util.*
import korlibs.math.*
import korlibs.memory.*
import kotlin.collections.indexOf

@OptIn(ExperimentalStdlibApi::class)
class FastByteArrayInputStream(val ba: ByteArray, offset: Int = 0, val start: Int = 0, val end: Int = ba.size) {
    @ExperimentalStdlibApi
    var offset = offset + start

    var position: Int
        get() = offset - start
        set(value) {
            offset = start + value
        }
    val length: Int get() = end - start
    val available: Int get() = end - offset
    val hasMore: Boolean get() = available > 0
    val eof: Boolean get() = !hasMore

    fun Int.coerceRange() = this.coerceIn(start, end)

    fun extractBytes(offset: Int, length: Int): ByteArray {
        val start = this.start + offset
        val end = start + length
        return ba.copyOfRange(start, end)
    }

    fun extractString(offset: Int, length: Int): String {
        //return extractBytes(offset, length).toString(charset)
        val start = this.start + offset
        val end = start + length
        return ba.copyOfRange(start, end).decodeToString()
    }

    fun readSlice(size: Int): FastByteArrayInputStream {
        val out = sliceWithSize(position, size)
        offset += size
        return out
    }

    fun sliceStart(offset: Int = 0) = FastByteArrayInputStream(ba, 0, (start + offset).coerceRange(), end)
    fun clone() = FastByteArrayInputStream(ba, position, start, end)
    fun sliceWithSize(offset: Int, len: Int) =
        FastByteArrayInputStream(ba, 0, (start + offset).coerceRange(), (start + offset + len).coerceRange())

    private fun offset(count: Int): Int {
        val out = offset
        offset += count
        return out
    }

    // Skipping
    fun skip(count: Int) {
        offset = (offset + count).coerceIn(start, end)
    }

    fun unread(count: Int): Unit = skip(-count)

    fun skipToAlign(count: Int) {
        val nextPosition = offset.nextAlignedTo(count)
        skip(nextPosition - offset)
    }

    fun readBytesExact(len: Int): ByteArray {
        val out = ba.copyOfRange(offset, offset + len)
        offset += len
        return out
    }

    fun readAll(): ByteArray = readBytesExact(available)

    // 8 bit
    fun readS8(): Int = ba.getS8(offset(1))
    fun readU8(): Int = ba.getU8(offset(1))

    // 16 bits
    fun readS16LE(): Int = ba.getS16LE(offset(2))

    fun readS16BE(): Int = ba.getS16BE(offset(2))
    fun readU16LE(): Int = ba.getU16LE(offset(2))
    fun readU16BE(): Int = ba.getU16BE(offset(2))

    // 24 bits
    fun readS24LE(): Int = ba.getS24LE(offset(3))
    fun readS24BE(): Int = ba.getS24BE(offset(3))
    fun readU24LE(): Int = ba.getU24LE(offset(3))
    fun readU24BE(): Int = ba.getU24BE(offset(3))

    // 32 bits
    fun readS32LE(): Int = ba.getS32LE(offset(4))
    fun readS32BE(): Int = ba.getS32BE(offset(4))
    fun readU32LE(): Long = ba.getU32LE(offset(4))
    fun readU32BE(): Long = ba.getU32BE(offset(4))

    // 32 bits FLOAT
    fun readF32LE(): Float = ba.getF32LE(offset(4))
    fun readF32BE(): Float = ba.getF32BE(offset(4))

    // 64 bits FLOAT
    fun readF64LE(): Double = ba.getF64LE(offset(8))
    fun readF64BE(): Double = ba.getF64BE(offset(8))

    // 64 bits Long
    fun readS64LE(): Long = ba.getS64LE(offset(8))
    fun readS64BE(): Long = ba.getS64BE(offset(8))

    // Bytes
    fun read(data: ByteArray, offset: Int = 0, count: Int = data.size - offset): Int {
        val readCount = count.coerceAtMost(available)
        arraycopy(this.ba, this.offset, data, offset, readCount)
        this.offset += count
        return readCount
    }

    fun readBytes(count: Int) = ba.getS8Array(offset(count), count)

    // Arrays
    fun readShortArrayLE(count: Int): ShortArray = ba.getS16ArrayLE(offset(count * 2), count)
    fun readShortArrayBE(count: Int): ShortArray = ba.getS16ArrayBE(offset(count * 2), count)

    fun readCharArrayLE(count: Int): CharArray = ba.getU16ArrayLE(offset(count * 2), count)
    fun readCharArrayBE(count: Int): CharArray = ba.getU16ArrayBE(offset(count * 2), count)

    fun readIntArrayLE(count: Int): IntArray = ba.getS32ArrayLE(offset(count * 4), count)
    fun readIntArrayBE(count: Int): IntArray = ba.getS32ArrayBE(offset(count * 4), count)

    fun readLongArrayLE(count: Int): LongArray = ba.getS64ArrayLE(offset(count * 8), count)
    fun readLongArrayBE(count: Int): LongArray = ba.getS64ArrayBE(offset(count * 8), count)

    fun readFloatArrayLE(count: Int): FloatArray = ba.getF32ArrayLE(offset(count * 4), count)
    fun readFloatArrayBE(count: Int): FloatArray = ba.getF32ArrayBE(offset(count * 4), count)

    fun readDoubleArrayLE(count: Int): DoubleArray = ba.getF64ArrayLE(offset(count * 8), count)
    fun readDoubleArrayBE(count: Int): DoubleArray = ba.getF64ArrayBE(offset(count * 8), count)

    // Variable Length
    fun readU_VL(): Int {
        var result = readU8()
        if ((result and 0x80) == 0) return result
        result = (result and 0x7f) or (readU8() shl 7)
        if ((result and 0x4000) == 0) return result
        result = (result and 0x3fff) or (readU8() shl 14)
        if ((result and 0x200000) == 0) return result
        result = (result and 0x1fffff) or (readU8() shl 21)
        if ((result and 0x10000000) == 0) return result
        result = (result and 0xfffffff) or (readU8() shl 28)
        return result
    }

    fun readS_VL(): Int {
        val v = readU_VL()
        val sign = ((v and 1) != 0)
        val uvalue = v ushr 1
        return if (sign) -uvalue - 1 else uvalue
    }

    // String
    fun extractString(offset: Int, length: Int, charset: Charset = UTF8): String {
        //return extractBytes(offset, length).toString(charset)
        val start = this.start + offset
        val end = start + length
        return ba.toString(charset, start, end)
    }

    fun readString(len: Int, charset: Charset = UTF8) = readBytes(len).toString(charset)

    fun readStringz(len: Int, charset: Charset = UTF8): String {
        val res = readBytes(len)
        val index = res.indexOf(0.toByte())
        return res.copyOf(if (index < 0) len else index).toString(charset)
    }

    fun readStringz(charset: Charset = UTF8): String {
        val startOffset = offset
        val index = ba.indexOf(0.toByte(), offset)
        val end = if (index >= 0) index else ba.size
        val str = ba.copyOfRange(startOffset, end).toString(charset)
        offset = if (index >= 0) end + 1 else end
        return str
    }

    fun readStringVL(charset: Charset = UTF8): String = readString(readU_VL(), charset)


    fun getAllBytes(): ByteArray = ba.copyOfRange(start, end)
    fun getBackingArrayUnsafe(): ByteArray = ba
    //fun toByteArray(): ByteArray = ba.copyOfRange(start, end)
}

fun ByteArray.openFastStream(offset: Int = 0): FastByteArrayInputStream = FastByteArrayInputStream(this, offset)




© 2015 - 2025 Weber Informatics LLC | Privacy Policy