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

nativeMain.kotlinx.io.core.IoBufferNative.kt Maven / Gradle / Ivy

There is a newer version: 0.1.16
Show newest version
@file:Suppress("RedundantModalityModifier")

package kotlinx.io.core

import kotlinx.cinterop.*
import kotlinx.io.pool.*
import platform.posix.memcpy
import platform.posix.memset
import platform.posix.size_t
import kotlinx.io.core.internal.*
import kotlin.native.concurrent.ThreadLocal

@PublishedApi
@SharedImmutable
internal val MAX_SIZE: size_t = size_t.MAX_VALUE

actual class IoBuffer internal constructor(
        internal var content: CPointer,
        private val contentCapacity: Int,
        internal actual val origin: IoBuffer?
) : Input, Output {
    @Deprecated(
        "Suppress warning.",
        level = DeprecationLevel.HIDDEN
    )
    @Suppress("unused")
    actual final override val doNotImplementInputButExtendAbstractInputInstead: Nothing
        get() = error("Should be never accessed.")

    @Deprecated(
        "Suppress warning.",
        level = DeprecationLevel.HIDDEN
    )
    @Suppress("unused")
    actual final override val doNotImplementOutputButExtendAbstractOutputInstead: Nothing
        get() = error("Should be never accessed.")

    internal var refCount = 1

    constructor(content: CPointer, contentCapacity: Int) : this(content, contentCapacity, null)

    internal var readPosition = 0
    internal var writePosition = 0
    private var limit = contentCapacity

    private var platformEndian = ByteOrder.BIG_ENDIAN === ByteOrder.nativeOrder()

    @ExperimentalIoApi
    actual var attachment: Any? = null
    actual var next: IoBuffer? = null

    actual val capacity: Int get() = contentCapacity
    actual val readRemaining: Int get() = writePosition - readPosition
    actual val writeRemaining: Int get() = limit - writePosition

    actual fun canRead() = writePosition > readPosition
    actual fun canWrite() = writePosition < limit

    override val endOfInput: Boolean get() = !canRead()

    init {
        require(contentCapacity >= 0) { "contentCapacity shouln't be negative: $contentCapacity" }
        require(this !== origin) { "origin shouldn't point to itself" }
    }

    @Deprecated("Read/write with readXXXLittleEndian/writeXXXLittleEndian or " +
        "do readXXX/writeXXX with X.reverseByteOrder() instead.")
    actual final override var byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN
        set(newOrder) {
            field = newOrder
            platformEndian = newOrder === ByteOrder.nativeOrder()
        }

    actual fun reserveStartGap(n: Int) {
        if (readPosition > 0) throw IllegalStateException("Start gap is already reserved")
        if (writePosition > 0) throw IllegalStateException("Start gap is already reserved")
        writePosition = n
        readPosition = n
    }

    actual fun reserveEndGap(n: Int) {
        if (limit != contentCapacity) throw IllegalStateException("End gap is already reserved")
        limit -= n
    }

    actual val startGap: Int get() = readPosition
    actual val endGap: Int get() = contentCapacity - limit


    actual final override fun readByte(): Byte {
        if (readRemaining < 0) throw IllegalStateException("No bytes available for read")
        val readPosition = readPosition
        val value = content[readPosition]
        this.readPosition = readPosition + 1
        return value
    }

    actual final override fun writeByte(v: Byte) {
        if (writeRemaining < 1) throw IllegalStateException("No space left for writing")
        content[writePosition] = v
        writePosition++
    }

    actual final override fun readShort(): Short {
        if (readRemaining < 2) throw IllegalStateException("Not enough bytes available to read a short")
        var value = (content + readPosition)!!.reinterpret()[0]
        if (!platformEndian) value = swap(value)
        readPosition += 2
        return value
    }

    actual final override fun writeShort(v: Short) {
        if (writeRemaining < 2) throw IllegalStateException("Not enough space left to write a short")
        var value = v
        if (!platformEndian) value = swap(value)
        (content + writePosition)!!.reinterpret()[0] = value
        writePosition += 2
    }

    actual final override fun readInt(): Int {
        if (readRemaining < 4) throw IllegalStateException("Not enough bytes available to read an int")
        var value = (content + readPosition)!!.reinterpret()[0]
        if (!platformEndian) value = swap(value)
        readPosition += 4
        return value
    }

    actual final override fun writeInt(v: Int) {
        if (writeRemaining < 4) throw IllegalStateException("Not enough space left to write an int")
        val value = if (platformEndian) v else swap(v)
        (content + writePosition)!!.reinterpret()[0] = value
        writePosition += 4
    }

    actual final override fun readFloat(): Float {
        if (readRemaining < 4) throw IllegalStateException("Not enough bytes available to read a float")

        val f = (content + readPosition)!!.reinterpret()[0]
        readPosition += 4
        return if (platformEndian) f else swap(f)
    }

    actual final override fun writeFloat(v: Float) {
        if (writeRemaining < 4) throw IllegalStateException("Not enough space left to write a float")
        val b = if (platformEndian) v else swap(v)
        (content + writePosition)!!.reinterpret()[0] = b

        writePosition += 4
    }

    actual final override fun readDouble(): Double {
        if (readRemaining < 8) throw IllegalStateException("Not enough bytes available to read a double")

        val b = (content + readPosition)!!.reinterpret()[0]
        readPosition += 8
        return if (platformEndian) b else swap(b)
    }

    actual final override fun writeDouble(v: Double) {
        if (writeRemaining < 8) throw IllegalStateException("Not enough space left to write a double")
        (content + writePosition)!!.reinterpret()[0] = if (platformEndian) v else swap(v)
        writePosition += 8
    }

    final override fun readFully(dst: CPointer, offset: Long, length: Long) {
        require(length <= readRemaining.toLong())
        require(length >= 0L) { "length shouldn't be negative: $length" }
        require(length <= Int.MAX_VALUE) { "length shouldn't be greater than Int.MAX_VALUE" }

        memcpy(dst + offset, content + readPosition, length.convert())
        readPosition += length.toInt()
    }

    final override fun readFully(dst: CPointer, offset: Int, length: Int) {
        require(length <= readRemaining) { "Not enough bytes available to read $length bytes" }
        require(length >= 0) { "length shouldn't be negative: $length" }

        memcpy(dst + offset, content + readPosition, length.convert())
        readPosition += length
    }

    final override fun writeFully(src: CPointer, offset: Int, length: Int) {
        require(length <= writeRemaining) { "Not enough space available to write $length bytes" }
        require(length >= 0) { "length shouldn't be negative: $length" }

        memcpy(content + writePosition, src + offset, length.convert())
        writePosition += length
    }

    final override fun writeFully(src: CPointer, offset: Long, length: Long) {
        require(length <= writeRemaining.toLong()) { "Not enough space available to write $length bytes" }
        require(length >= 0) { "length shouldn't be negative: $length" }
        require(length.convert() <= size_t.MAX_VALUE) { "length shouldn't be greater than ${size_t.MAX_VALUE}" }

        memcpy(content + writePosition, src + offset, length.convert())
        writePosition += length.toInt()
    }

    @Deprecated("Use readFully instead", ReplaceWith("readFully(dst, offset, length)"), level = DeprecationLevel.ERROR)
    actual fun read(dst: ByteArray, offset: Int, length: Int) {
        readFully(dst, offset, length)
    }

    actual final override fun readFully(dst: ByteArray, offset: Int, length: Int) {
        require(length <= readRemaining) { "Not enough bytes available to read $length bytes" }
        require(length >= 0) { "length shouldn't be negative: $length" }
        require(offset >= 0) { "offset shouldn't be negative: $offset" }
        require(offset + length <= dst.size) { "offset ($offset) + length ($length) > dst.size (${dst.size})" }

        if (length == 0) return

        dst.usePinned {
            val address = it.addressOf(offset)
            memcpy(address, content + readPosition, length.convert())
        }

        readPosition += length
    }

    actual final override fun readFully(dst: ShortArray, offset: Int, length: Int) {
        require(length * 2 <= readRemaining) { "Not enough bytes available to read $length short int numbers (16)" }
        require(length >= 0) { "length shouldn't be negative: $length" }
        require(offset >= 0) { "offset shouldn't be negative: $offset" }
        require(offset + length <= dst.size) { "offset ($offset) + length ($length) > dst.size (${dst.size})" }

        if (length == 0) return

        if (platformEndian) {
            dst.usePinned {
                memcpy(it.addressOf(offset), content + readPosition, (length * 2).convert())
            }
        } else {
            val ptr = (content + readPosition)!!.reinterpret()
            for (i in 0 .. length - 1) {
                dst[offset + i] = swap(ptr[i])
            }
        }
        readPosition += length * 2
    }

    actual final override fun readFully(dst: IntArray, offset: Int, length: Int) {
        require(length * 4 <= readRemaining) { "Not enough bytes available to read $length int numbers (32)" }
        require(length >= 0) { "length shouldn't be negative: $length" }
        require(offset >= 0) { "offset shouldn't be negative: $offset" }
        require(offset + length <= dst.size) { "offset ($offset) + length ($length) > dst.size (${dst.size})" }

        if (length == 0) return

        if (platformEndian) {
            dst.usePinned {
                memcpy(it.addressOf(offset), content + readPosition, (length * 4).convert())
            }
        } else {
            val ptr = (content + readPosition)!!.reinterpret()
            for (i in 0 .. length - 1) {
                dst[offset + i] = swap(ptr[i])
            }
        }
        readPosition += length * 4
    }

    actual final override fun readFully(dst: LongArray, offset: Int, length: Int) {
        require(length * 8 <= readRemaining) { "Not enough bytes available to read $length long int numbers (64)" }
        require(length >= 0) { "length shouldn't be negative: $length" }
        require(offset >= 0) { "offset shouldn't be negative: $offset" }
        require(offset + length <= dst.size) { "offset ($offset) + length ($length) > dst.size (${dst.size})" }

        if (length == 0) return

        if (platformEndian) {
            dst.usePinned {
                memcpy(it.addressOf(offset), content + readPosition, (length * 8).convert())
            }
        } else {
            val ptr = (content + readPosition)!!.reinterpret()
            for (i in 0 .. length - 1) {
                dst[offset + i] = swap(ptr[i])
            }
        }
        readPosition += length * 8
    }

    actual final override fun readFully(dst: FloatArray, offset: Int, length: Int) {
        require(length * 4 <= readRemaining) { "Not enough bytes available to read $length float numbers" }
        require(length >= 0) { "length shouldn't be negative: $length" }
        require(offset >= 0) { "offset shouldn't be negative: $offset" }
        require(offset + length <= dst.size) { "offset ($offset) + length ($length) > dst.size (${dst.size})" }

        if (length == 0) return

        if (platformEndian) {
            dst.usePinned {
                memcpy(it.addressOf(offset), content + readPosition, (length * 4).convert())
            }
        } else {
            val ptr = (content + readPosition)!!.reinterpret()
            for (i in 0 .. length - 1) {
                dst[offset + i] = swap(ptr[i])
            }
        }
        readPosition += length * 4
    }

    actual final override fun readFully(dst: DoubleArray, offset: Int, length: Int) {
        require(length * 8 <= readRemaining) { "Not enough bytes available to read $length double numbers" }
        require(length >= 0) { "length shouldn't be negative: $length" }
        require(offset >= 0) { "offset shouldn't be negative: $offset" }
        require(offset + length <= dst.size) { "offset ($offset) + length ($length) > dst.size (${dst.size})" }

        if (length == 0) return

        if (platformEndian) {
            dst.usePinned {
                memcpy(it.addressOf(offset), content + readPosition, (length * 8).convert())
            }
        } else {
            val ptr = (content + readPosition)!!.reinterpret()
            for (i in 0 .. length - 1) {
                dst[offset + i] = swap(ptr[i])
            }
        }
        readPosition += length * 8
    }

    actual final override fun readFully(dst: IoBuffer, length: Int) {
        require(length <= readRemaining) { "Not enough bytes available to read $length bytes" }
        require(length <= dst.writeRemaining) { "Not enough space in the destination buffer to read $length bytes" }
        require(length >= 0) { "length shouldn't be negative: $length" }

        memcpy(dst.content + dst.writePosition, content + readPosition, length.convert())
        readPosition += length
        dst.writePosition += length
    }


    actual final override fun readAvailable(dst: ByteArray, offset: Int, length: Int): Int {
        require(length >= 0) { "length shouldn't be negative: $length" }
        require(offset >= 0) { "offset shouldn't be negative: $offset" }
        require(offset + length <= dst.size) { "offset ($offset) + length ($length) > dst.size (${dst.size})" }

        if (length == 0) return 0

        return dst.usePinned {
            val copySize = minOf(length, readRemaining)
            memcpy(it.addressOf(offset), content + readPosition, copySize.convert())
            readPosition += copySize
            copySize
        }
    }

    actual final override fun readAvailable(dst: ShortArray, offset: Int, length: Int): Int {
        require(length >= 0) { "length shouldn't be negative: $length" }
        require(offset >= 0) { "offset shouldn't be negative: $offset" }
        require(offset + length <= dst.size) { "offset ($offset) + length ($length) > dst.size (${dst.size})" }

        if (length == 0) return 0
        val copySize = minOf(length, readRemaining shr 1)

        if (platformEndian) {
            dst.usePinned {
                memcpy(it.addressOf(offset), content + readPosition, (copySize * 2).convert())
            }
        } else {
            val ptr = (content + readPosition)!!.reinterpret()
            for (i in 0 .. copySize - 1) {
                dst[offset + i] = swap(ptr[i])
            }
        }

        readPosition += copySize * 2

        return copySize
    }

    actual final override fun readAvailable(dst: IntArray, offset: Int, length: Int): Int {
        require(length >= 0) { "length shouldn't be negative: $length" }
        require(offset >= 0) { "offset shouldn't be negative: $offset" }
        require(offset + length <= dst.size) { "offset ($offset) + length ($length) > dst.size (${dst.size})" }

        if (length == 0) return 0
        val copySize = minOf(length, readRemaining shr 2)

        if (platformEndian) {
            dst.usePinned {
                memcpy(it.addressOf(offset), content + readPosition, (copySize * 4).convert())
            }
        } else {
            val ptr = (content + readPosition)!!.reinterpret()
            for (i in 0 .. copySize - 1) {
                dst[offset + i] = swap(ptr[i])
            }
        }

        readPosition += copySize * 4

        return copySize
    }

    actual final override fun readAvailable(dst: LongArray, offset: Int, length: Int): Int {
        require(length >= 0) { "length shouldn't be negative: $length" }
        require(offset >= 0) { "offset shouldn't be negative: $offset" }
        require(offset + length <= dst.size) { "offset ($offset) + length ($length) > dst.size (${dst.size})" }

        if (length == 0) return 0
        val copySize = minOf(length, readRemaining shr 3)

        if (platformEndian) {
            dst.usePinned {
                memcpy(it.addressOf(offset), content + readPosition, (copySize * 8).convert())
            }
        } else {
            val ptr = (content + readPosition)!!.reinterpret()
            for (i in 0 .. copySize - 1) {
                dst[offset + i] = swap(ptr[i])
            }
        }

        readPosition += copySize * 8

        return copySize
    }

    actual final override fun readAvailable(dst: FloatArray, offset: Int, length: Int): Int {
        require(length >= 0) { "length shouldn't be negative: $length" }
        require(offset >= 0) { "offset shouldn't be negative: $offset" }
        require(offset + length <= dst.size) { "offset ($offset) + length ($length) > dst.size (${dst.size})" }

        if (length == 0) return 0
        val copySize = minOf(length, readRemaining shr 2)

        if (platformEndian) {
            dst.usePinned {
                memcpy(it.addressOf(offset), content + readPosition, (copySize * 4).convert())
            }
        } else {
            val ptr = (content + readPosition)!!.reinterpret()
            for (i in 0 .. copySize - 1) {
                dst[offset + i] = swap(ptr[i])
            }
        }

        readPosition += copySize * 4

        return copySize
    }

    actual final override fun readAvailable(dst: DoubleArray, offset: Int, length: Int): Int {
        require(length >= 0) { "length shouldn't be negative: $length" }
        require(offset >= 0) { "offset shouldn't be negative: $offset" }
        require(offset + length <= dst.size) { "offset ($offset) + length ($length) > dst.size (${dst.size})" }

        if (length == 0) return 0
        val copySize = minOf(length, readRemaining shr 3)

        if (platformEndian) {
            dst.usePinned {
                memcpy(it.addressOf(offset), content + readPosition, (copySize * 8).convert())
            }
        } else {
            val ptr = (content + readPosition)!!.reinterpret()
            for (i in 0 .. copySize - 1) {
                dst[offset + i] = swap(ptr[i])
            }
        }

        readPosition += copySize * 8

        return copySize
    }

    actual final override fun readAvailable(dst: IoBuffer, length: Int): Int {
        require(length <= dst.writeRemaining) { "Not enough space in the dst buffer to write $length bytes" }
        require(length >= 0) { "length shouldn't be negative: $length" }

        val copySize = minOf(length, readRemaining)
        memcpy(dst.content + dst.writePosition, content + readPosition, copySize.convert())
        readPosition += length
        dst.writePosition += length

        return copySize
    }

    final override fun readAvailable(dst: CPointer, offset: Int, length: Int): Int {
        require(length >= 0) { "length shouldn't be negative: $length" }

        val copySize = minOf(length, readRemaining)
        memcpy(dst + offset, content + readPosition, copySize.convert())
        readPosition += length

        return copySize
    }

    final override fun readAvailable(dst: CPointer, offset: Long, length: Long): Long {
        require(length >= 0) { "length shouldn't be negative: $length" }
        require(length <= Int.MAX_VALUE) { "length shouldn't be greater than Int.MAX_VALUE" }

        val copySize = minOf(length, readRemaining.toLong())
        memcpy(dst + offset, content + readPosition, copySize.convert())
        readPosition += length.toInt()

        return copySize
    }

    /**
     * Apply [block] to a native pointer for writing to the buffer. Lambda should return number of bytes were written.
     * @return number of bytes written
     */
    fun writeDirect(block: (CPointer) -> Int): Int {
        val rc = block((content + writePosition)!!)
        check(rc >= 0) { "block function should return non-negative results: $rc" }
        check(rc <= writeRemaining)
        writePosition += rc
        return rc
    }

    /**
     * Apply [block] to a native pointer for reading from the buffer. Lambda should return number of bytes were read.
     * @return number of bytes read
     */
    fun readDirect(block: (CPointer) -> Int): Int {
        val rc = block((content + readPosition)!!)
        check(rc >= 0) { "block function should return non-negative results: $rc" }
        check(rc <= readRemaining) { "result value is too large: $rc > $readRemaining" }
        readPosition += rc
        return rc
    }

    @Deprecated("Use writeFully instead", level = DeprecationLevel.ERROR)
    actual final fun write(array: ByteArray, offset: Int, length: Int) {
        writeFully(array, offset, length)
    }

    actual final override fun writeFully(src: ByteArray, offset: Int, length: Int) {
        require(length <= writeRemaining) { "Not enough space to write $length bytes" }
        require(length >= 0) { "length shouldn't be negative: $length" }
        require(offset >= 0) { "offset shouldn't be negative: $offset" }
        require(offset + length <= src.size) { "offset ($offset) + length ($length) > src.size (${src.size})" }

        if (length == 0) return

        src.usePinned {
            val address = it.addressOf(offset)
            memcpy(content + writePosition, address, length.convert())
        }

        writePosition += length
    }

    actual final override fun writeFully(src: ShortArray, offset: Int, length: Int) {
        require(length * 2 <= writeRemaining) { "Not enough space to write $length short int numbers (16)" }
        require(length >= 0) { "length shouldn't be negative: $length" }
        require(offset >= 0) { "offset shouldn't be negative: $offset" }
        require(offset + length <= src.size) { "offset ($offset) + length ($length) > src.size (${src.size})" }

        if (length == 0) return

        if (platformEndian) {
            src.usePinned {
                memcpy(content + writePosition, it.addressOf(offset), (length * 2).convert())
            }
        } else {
            val buffer = (content + writePosition)!!.reinterpret()
            for (i in 0 .. length - 1){
                buffer[i] = swap(src[i + offset])
            }
        }

        writePosition += length * 2
    }

    actual final override fun writeFully(src: IntArray, offset: Int, length: Int) {
        require(length * 4 <= writeRemaining) { "Not enough space to write $length int numbers (32)"}
        require(length >= 0) { "length shouldn't be negative: $length" }
        require(offset >= 0) { "offset shouldn't be negative: $offset" }
        require(offset + length <= src.size) { "offset ($offset) + length ($length) > src.size (${src.size})" }

        if (length == 0) return

        if (platformEndian) {
            src.usePinned {
                memcpy(content + writePosition, it.addressOf(offset), (length * 4).convert())
            }
        } else {
            val buffer = (content + writePosition)!!.reinterpret()
            for (i in 0 .. length - 1){
                buffer[i] = swap(src[i + offset])
            }
        }

        writePosition += length * 4
    }

    actual final override fun writeFully(src: LongArray, offset: Int, length: Int) {
        require(length * 8 <= writeRemaining) { "Not enough space to write $length long int numbers (64)"}
        require(length >= 0) { "length shouldn't be negative: $length" }
        require(offset >= 0) { "offset shouldn't be negative: $offset" }
        require(offset + length <= src.size) { "offset ($offset) + length ($length) > src.size (${src.size})" }

        if (length == 0) return

        if (platformEndian) {
            src.usePinned {
                memcpy(content + writePosition, it.addressOf(offset), (length * 8).convert())
            }
        } else {
            val buffer = (content + writePosition)!!.reinterpret()
            for (i in 0 .. length - 1){
                buffer[i] = swap(src[i + offset])
            }
        }

        writePosition += length * 8
    }

    actual final override fun writeFully(src: FloatArray, offset: Int, length: Int) {
        require(length * 4 <= writeRemaining) { "Not enough space to write $length float numbers (32)" }
        require(length >= 0) { "length shouldn't be negative: $length" }
        require(offset >= 0) { "offset shouldn't be negative: $offset" }
        require(offset + length <= src.size) { "offset ($offset) + length ($length) > src.size (${src.size})" }

        if (length == 0) return

        if (platformEndian) {
            src.usePinned {
                memcpy(content + writePosition, it.addressOf(offset), (length * 4).convert())
            }
        } else {
            val buffer = (content + writePosition)!!.reinterpret()
            for (i in 0 .. length - 1){
                buffer[i] = swap(src[i + offset])
            }
        }

        writePosition += length * 4
    }

    actual final override fun writeFully(src: DoubleArray, offset: Int, length: Int) {
        require(length * 8 <= writeRemaining) { "Not enough space to write $length double numbers (64)"}
        require(length >= 0) { "length shouldn't be negative: $length" }
        require(offset >= 0) { "offset shouldn't be negative: $offset" }
        require(offset + length <= src.size) { "offset ($offset) + length ($length) > src.size (${src.size})" }

        if (length == 0) return

        if (platformEndian) {
            src.usePinned {
                memcpy(content + writePosition, it.addressOf(offset), (length * 8).convert())
            }
        } else {
            val buffer = (content + writePosition)!!.reinterpret()
            for (i in 0 .. length - 1){
                buffer[i] = swap(src[i + offset])
            }
        }

        writePosition += length * 8
    }

    actual final override fun writeFully(src: IoBuffer, length: Int) {
        require(length <= src.readRemaining) { "length is too large: not enough bytes to read $length > ${src.readRemaining}"}
        require(length <= writeRemaining) { "length is too large: not enough room to write $length > $writeRemaining" }

        memcpy(content + writePosition, src.content + src.readPosition, length.convert())

        src.readPosition += length
        writePosition += length
    }

    actual final override fun fill(n: Long, v: Byte) {
        require(n <= writeRemaining.toLong())
        require(n >= 0) { "n shouldn't be negative: $n" }
        require(n < Int.MAX_VALUE)

        memset(content + writePosition, v.toInt() and 0xff, n.convert())
        writePosition += n.convert()
    }

    actual final override fun readLong(): Long {
        if (readRemaining < 8) throw IllegalStateException("Not enough bytes available to read a long")
        val m = 0xffffffff
        val a = readInt().toLong() and m
        val b = readInt().toLong() and m

        @Suppress("DEPRECATION")
        return if (byteOrder === ByteOrder.LITTLE_ENDIAN) {
            (b shl 32) or a
        } else {
            (a shl 32) or b
        }
    }

    actual final override fun writeLong(v: Long) {
        if (writeRemaining < 8) throw IllegalStateException("Not enough space left to write a long")
        val m = 0xffffffff
        val a = (v shr 32).toInt()
        val b = (v and m).toInt()

        @Suppress("DEPRECATION")
        if (byteOrder === ByteOrder.LITTLE_ENDIAN) {
            writeInt(b)
            writeInt(a)
        } else {
            writeInt(a)
            writeInt(b)
        }
    }

    actual final override fun append(csq: CharSequence?, start: Int, end: Int): Appendable {
        val idx = appendChars(csq ?: "null", start, end)
        if (idx != end) throw IllegalStateException("Not enough free space to append char sequence")
        return this
    }

    actual final override fun append(csq: CharSequence?): Appendable {
        return if (csq == null) append("null") else append(csq, 0, csq.length)
    }

    actual final override fun append(csq: CharArray, start: Int, end: Int): Appendable {
        val idx = appendChars(csq, start, end)

        if (idx != end) throw IllegalStateException("Not enough free space to append char sequence")
        return this
    }

    actual override fun append(c: Char): Appendable {
        val wp = writePosition
        val s = content.putUtf8Char(c.toInt(), limit - wp, wp)
        if (s == 0) notEnoughFreeSpace(c)
        writePosition = wp + s
        return this
    }

    private fun notEnoughFreeSpace(c: Char): Nothing {
        throw IllegalStateException("Not Enough free space to append character '$c', remaining $writeRemaining bytes")
    }

    actual fun appendChars(csq: CharArray, start: Int, end: Int): Int {
        val i8 = content
        var wp = writePosition
        val l = limit
        var rc = end

        if (start == end || wp == l) return start

        for (idx in start until end) {
            val ch = csq[idx].toInt()
            if (ch > 0x7f || wp >= l) {
                rc = idx
                break
            }

            i8[wp++] = ch.toByte()
        }

        if (rc >= end || wp == l) {
            writePosition = wp
            return rc
        }

        return appendCharsUtf8(csq, rc, end, wp)
    }

    private fun appendCharsUtf8(csq: CharArray, start: Int, end: Int, wp0: Int): Int {
        val i8 = content
        val l = limit
        var wp = wp0
        var idx = start

        while (idx < end) {
            val ch = csq[idx++].toInt()

            val size = if (ch.isSurrogateCodePoint()) i8.putUtf8CharSurrogate(ch, csq[idx++].toInt(), l - wp, wp)
            else i8.putUtf8Char(ch, l - wp, wp)

            if (size == 0) {
                return appendCharFailed(ch, idx, wp)
            }

            wp += size
        }

        writePosition = wp
        return end
    }

    actual fun appendChars(csq: CharSequence, start: Int, end: Int): Int {
        val i8 = content
        var wp = writePosition
        val l = limit
        var rc = end

        if (start == end || wp == l) return start

        for (idx in start until end) {
            val ch = csq[idx].toInt()
            if (ch > 0x7f || wp >= l) {
                rc = idx
                break
            }

            i8[wp++] = ch.toByte()
        }

        if (rc >= end || wp == limit) {
            writePosition = wp
            return rc
        }

        return appendCharsUtf8(csq, rc, end, wp)
    }

    private fun appendCharsUtf8(csq: CharSequence, start: Int, end: Int, wp0: Int): Int {
        val i8 = content
        val l = limit
        var wp = wp0
        var idx = start

        while (idx < end) {
            val ch = csq[idx++].toInt()
            val remaining = l - wp
            val size = if (ch.isSurrogateCodePoint()) i8.putUtf8CharSurrogate(ch, csq[idx++].toInt(), remaining, wp)
            else i8.putUtf8Char(ch, remaining, wp)

            if (size == 0) {
                return appendCharFailed(ch, idx, wp)
            }

            wp += size
        }

        writePosition = wp
        return end
    }

    private fun appendCharFailed(ch: Int, idx: Int, wp: Int): Int {
        writePosition = wp
        return if (ch.isSurrogateCodePoint()) idx - 2 else idx - 1
    }

    @Suppress("NOTHING_TO_INLINE")
    private inline fun CPointer.putUtf8Char(v: Int, remaining: Int, wp: Int): Int {
        return when {
            v in 1..0x7f -> {
                if (remaining < 1) return 0
                this[wp] = v.toByte()
                1
            }
            v > 0xffff -> {
                if (remaining < 4) return 0
                this[wp    ] = (0xf0 or ((v shr 18) and 0x3f)).toByte()
                this[wp + 1] = (0x80 or ((v shr 12) and 0x3f)).toByte()
                this[wp + 2] = (0x80 or ((v shr  6) and 0x3f)).toByte()
                this[wp + 3] = (0x80 or ( v         and 0x3f)).toByte()
                4
            }
            v > 0x7ff -> {
                if (remaining < 3) return 0
                this[wp    ] = (0xe0 or ((v shr 12) and 0x0f)).toByte()
                this[wp + 1] = (0x80 or ((v shr  6) and 0x3f)).toByte()
                this[wp + 2] = (0x80 or ( v         and 0x3f)).toByte()
                3
            }
            else -> {
                if (remaining < 2) return 0
                this[wp    ] = (0xc0 or ((v shr  6) and 0x1f)).toByte()
                this[wp + 1] = (0x80 or ( v         and 0x3f)).toByte()
                2
            }
        }
    }

    private fun CPointer.putUtf8CharSurrogate(high: Int, low: Int, remaining: Int, wp: Int): Int {
        val highValue = (high and 0x7ff) shl 10
        val lowValue = (low and 0x3ff)
        val value = 0x010000 or (highValue or lowValue)

        return putUtf8Char(value, remaining, wp)
    }

    @Suppress("NOTHING_TO_INLINE")
    private inline fun Int.isSurrogateCodePoint() = this in 55296..57343

    @Deprecated("Use writeFully instead", ReplaceWith("writeFully(src, length)"), level = DeprecationLevel.ERROR)
    actual fun writeBuffer(src: IoBuffer, length: Int): Int {
        writeFully(src, length)
        return length
    }

    internal actual fun restoreStartGap(n: Int) {
        val rp = readPosition
        if (rp < n) {
            throw IllegalArgumentException("Can't restore start gap: $n bytes were not reserved before")
        }

        readPosition = rp - n
    }

    internal actual fun restoreEndGap(n: Int) {
        val newLimit = limit - n
        limit = newLimit
        if (writePosition > newLimit) {
            writePosition = newLimit
        }
        if (readPosition > newLimit) {
            readPosition = newLimit
        }
    }

    internal actual fun writeBufferPrepend(other: IoBuffer) {
        val size = other.readRemaining
        require(size <= startGap) { "size should be greater than startGap (size = $size, startGap = $startGap)" }

        memcpy(content + (readPosition - size), other.content + other.readPosition, size.convert())

        readPosition -= size
        other.readPosition += size
    }

    internal actual fun writeBufferAppend(other: IoBuffer, maxSize: Int) {
        val size = minOf(other.readRemaining, maxSize)
        require(size <= writeRemaining + endGap) { "size should be greater than write space + end gap (size = $size, " +
                "writeRemaining = $writeRemaining, endGap = $endGap, rem+gap = ${writeRemaining + endGap}" }

        memcpy(content + writePosition, other.content + other.readPosition, size.convert())

        writePosition += size
        if (writePosition > limit) {
            limit = writePosition
        }
        other.readPosition += size
    }

    actual final override fun tryPeek(): Int {
        val readPosition = readPosition
        val writePosition = writePosition
        if (readPosition == writePosition) return -1

        return content[readPosition].toInt() and 0xff
    }

    @Deprecated("Binary compatibility.", level = DeprecationLevel.HIDDEN)
    actual final override fun peekTo(buffer: IoBuffer): Int {
        return peekTo(buffer)
    }

    actual fun discardExact(n: Int) {
        if (discard(n.toLong()) != n.toLong()) throw EOFException("Unable to discard $n")
    }

    @Deprecated("Use discardExact instead.")
    actual final override fun discard(n: Long): Long {
        val step = minOf(readRemaining.toLong(), n).toInt()
        readPosition += step
        return step.toLong()
    }

    actual fun resetForWrite() {
        resetForWrite(contentCapacity)
    }

    actual fun resetForWrite(limit: Int) {
        require(limit >= 0) { "Limit shouldn't be negative: $limit" }
        require(limit <= contentCapacity) { "Limit shouldn't be greater than buffers capacity" }

        readPosition = 0
        writePosition = 0
        this.limit = limit
    }

    actual fun resetForRead() {
        readPosition = 0
        limit = contentCapacity
        writePosition = limit
    }

    actual fun pushBack(n: Int) {
        if (readPosition < n) throw IllegalStateException("Nothing to push back")
        readPosition -= n
    }

    internal fun unlink() {
        if (refCount != 0) throw IllegalStateException("Unable to unlink buffers: buffer view is in use")
        content = EmptyBuffer
        resetForWrite(0)
    }

    private fun acquire() {
        val v = refCount
        if (v == 0) throw IllegalStateException("Failed to acquire buffer: buffer has been already released")
        refCount = v + 1
    }

    private fun release(): Boolean {
        if (this === Empty) throw IllegalStateException("attempted to release IoBuffer.Empty")

        val v = refCount
        if (v == 0) throw IllegalStateException("Unable to release: buffer has been already released")
        val newCount = v - 1
        refCount = newCount
        return newCount == 0
    }

    @ExperimentalIoApi
    actual fun isExclusivelyOwned(): Boolean = refCount == 1

    actual fun makeView(): IoBuffer {
        if (this === Empty) return this

        val o = origin ?: this
        o.acquire()

        val view = IoBuffer(content, contentCapacity, o)
        view.attachment = attachment
        view.readPosition = readPosition
        view.writePosition = writePosition
        view.limit = limit

        return view
    }

    actual fun release(pool: ObjectPool) {
        if (release()) {
            resetForWrite()

            if (origin != null) {
                unlink()
                origin.release(pool)
            } else {
                pool.recycle(this)
            }
        }
    }

    actual final override fun flush() {
    }

    actual override fun close() {
        throw UnsupportedOperationException("close for buffer view is not supported")
    }

    override fun toString(): String =
        "Buffer[readable = $readRemaining, writable = $writeRemaining, startGap = $startGap, endGap = $endGap]"

    actual companion object {
        /**
         * Number of bytes usually reserved in the end of chunk
         * when several instances of [IoBuffer] are connected into a chain (usually inside of [ByteReadPacket]
         * or [BytePacketBuilder])
         */
        @Deprecated("This implementation detail is going to become internal.")
        actual val ReservedSize: Int = 8

        internal val EmptyBuffer = nativeHeap.allocArray(0)

        actual val Empty = IoBuffer(EmptyBuffer, 0, null)

        actual val Pool: ObjectPool get() = NoPool // BufferPoolNativeWorkaround

        actual val NoPool: ObjectPool = object : NoPoolImpl() {
            override fun borrow(): IoBuffer {
                val content = nativeHeap.allocArray(BUFFER_VIEW_SIZE)
                return IoBuffer(content, BUFFER_VIEW_SIZE, null)
            }

            override fun recycle(instance: IoBuffer) {
                require(instance.refCount == 0) { "Couldn't dispose buffer: it is still in-use: refCount = ${instance.refCount}" }
                require(instance.content !== EmptyBuffer) { "Couldn't dispose empty buffer" }
                nativeHeap.free(instance.content)
                instance.content = EmptyBuffer
            }
        }

        internal val NoPoolForManaged: ObjectPool = object : NoPoolImpl() {
            override fun borrow(): IoBuffer {
                error("You can't borrow an instance from this pool: use it only for manually created")
            }

            override fun recycle(instance: IoBuffer) {
                require(instance.refCount == 0) { "Couldn't dispose buffer: it is still in-use: refCount = ${instance.refCount}" }
                require(instance.content !== EmptyBuffer) { "Couldn't dispose empty buffer" }
                instance.content = EmptyBuffer
            }
        }

        actual val EmptyPool: ObjectPool = EmptyBufferPoolImpl
    }
}

@Suppress("NOTHING_TO_INLINE")
internal inline fun swap(s: Short): Short = (((s.toInt() and 0xff) shl 8) or ((s.toInt() and 0xffff) ushr 8)).toShort()

@Suppress("NOTHING_TO_INLINE")
internal inline fun swap(s: Int): Int =
    (swap((s and 0xffff).toShort()).toInt() shl 16) or (swap((s ushr 16).toShort()).toInt() and 0xffff)

@Suppress("NOTHING_TO_INLINE")
internal inline fun swap(s: Long): Long =
    (swap((s and 0xffffffff).toInt()).toLong() shl 32) or (swap((s ushr 32).toInt()).toLong() and 0xffffffff)

@Suppress("NOTHING_TO_INLINE")
internal inline fun swap(s: Float): Float = Float.fromBits(swap(s.toRawBits()))

@Suppress("NOTHING_TO_INLINE")
internal inline fun swap(s: Double): Double = Double.fromBits(swap(s.toRawBits()))

@ThreadLocal
private object BufferPoolNativeWorkaround : DefaultPool(BUFFER_VIEW_POOL_SIZE) {
    override fun produceInstance(): IoBuffer {
        val buffer = nativeHeap.allocArray(BUFFER_VIEW_SIZE)
        return IoBuffer(buffer, BUFFER_VIEW_SIZE, null)
    }

    override fun clearInstance(instance: IoBuffer): IoBuffer {
        return super.clearInstance(instance).apply {
            instance.resetForWrite()
            instance.next = null
            instance.attachment = null

            if (instance.refCount != 0) throw IllegalStateException("Unable to clear instance: refCount is ${instance.refCount} != 0")
            instance.refCount = 1
        }
    }

    override fun validateInstance(instance: IoBuffer) {
        super.validateInstance(instance)

        require(instance.refCount == 0) { "unable to recycle buffer: buffer view is in use (refCount = ${instance.refCount})"}
        require(instance.origin == null) { "Unable to recycle buffer view: view copy shouldn't be recycled" }
    }

    override fun disposeInstance(instance: IoBuffer) {
        require(instance.refCount == 0) { "Couldn't dispose buffer: it is still in-use: refCount = ${instance.refCount}" }
        require(instance.content !== IoBuffer.EmptyBuffer) { "Couldn't dispose empty buffer" }

        nativeHeap.free(instance.content)
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy