jsMain.kotlinx.io.core.IoBufferJS.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kotlinx-io-js Show documentation
Show all versions of kotlinx-io-js Show documentation
IO support libraries for Kotlin
@file:Suppress("ReplaceRangeToWithUntil", "RedundantModalityModifier")
package kotlinx.io.core
import kotlinx.io.core.internal.*
import kotlinx.io.js.*
import kotlinx.io.pool.*
import org.khronos.webgl.*
actual class IoBuffer internal constructor(
internal var content: ArrayBuffer,
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.")
private var refCount = 1
internal var readPosition = 0
internal var writePosition = 0
private var limit = content.byteLength
private var view = if (content === EmptyBuffer) EmptyDataView else DataView(content)
private var i8 = if (content === EmptyBuffer) Empty8 else Int8Array(content, 0, limit)
private var i16 = if (content === EmptyBuffer) Empty16 else Int16Array(content, 0, limit / 2)
private var i32 = if (content === EmptyBuffer) Empty32 else Int32Array(content, 0, limit / 4)
private var f32 = if (content === EmptyBuffer) EmptyF32 else Float32Array(content, 0, limit / 4)
private var f64 = if (content === EmptyBuffer) EmptyF64 else Float64Array(content, 0, limit / 8)
private var littleEndian = false
private var platformEndian = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN
init {
require(origin !== this) { "origin shouldn't point to itself" }
}
@ExperimentalIoApi
actual var attachment: Any? = null
actual var next: IoBuffer? = null
override val endOfInput: Boolean get() = writePosition == readPosition
/**
* Backing buffer capacity. Value for released buffer is unspecified
*/
actual val capacity: Int get() = content.byteLength
actual val readRemaining get() = writePosition - readPosition
actual val writeRemaining get() = limit - writePosition
actual fun canRead() = writePosition > readPosition
actual fun canWrite() = writePosition < limit
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 != content.byteLength) throw IllegalStateException("End gap is already reserved")
limit -= n
}
actual val startGap: Int get() = readPosition
actual val endGap: Int get() = content.byteLength - limit
@Deprecated("Read/write with readXXXLittleEndian/writeXXXLittleEndian or " +
"do readXXX/writeXXX with X.reverseByteOrder() instead.")
actual final override var byteOrder: ByteOrder
get() = if (littleEndian) ByteOrder.LITTLE_ENDIAN else ByteOrder.BIG_ENDIAN
set(value) {
littleEndian = when (value) {
ByteOrder.BIG_ENDIAN -> false
ByteOrder.LITTLE_ENDIAN -> true
}
platformEndian = value === ByteOrder.nativeOrder()
}
actual final override fun readByte(): Byte {
if (readRemaining < 0) throw IllegalStateException("No bytes available for read")
val value = i8[readPosition]
readPosition++
return value
}
actual final override fun writeByte(v: Byte) {
if (writeRemaining < 1) throw IllegalStateException("No space left for writing")
i8[writePosition] = v
writePosition++
}
actual final override fun readShort(): Short {
if (readRemaining < 2) throw IllegalStateException("Not enough bytes available to read a short")
val value = view.getInt16(readPosition, littleEndian)
readPosition += 2
return value
}
actual final override fun writeShort(v: Short) {
if (writeRemaining < 2) throw IllegalStateException("Not enough space left to write a short")
view.setInt16(writePosition, v, littleEndian)
writePosition += 2
}
actual final override fun readInt(): Int {
if (readRemaining < 4) throw IllegalStateException("Not enough bytes available to read an int")
return readIntUnsafe()
}
private fun readIntUnsafe(): Int {
val value = view.getInt32(readPosition, littleEndian)
readPosition += 4
return value
}
actual final override fun writeInt(v: Int) {
if (writeRemaining < 4) throw IllegalStateException("Not enough space left to write an int")
view.setInt32(writePosition, v, littleEndian)
writePosition += 4
}
actual final override fun readFloat(): Float {
if (readRemaining < 4) throw IllegalStateException("Not enough bytes available to read a float")
val value = view.getFloat32(readPosition, littleEndian)
readPosition += 4
return value
}
actual final override fun writeFloat(v: Float) {
if (writeRemaining < 4) throw IllegalStateException("Not enough space left to write a float")
view.setFloat32(writePosition, v, littleEndian)
writePosition += 4
}
actual final override fun readDouble(): Double {
if (readRemaining < 8) throw IllegalStateException("Not enough bytes available to read a double")
val value = view.getFloat64(readPosition, littleEndian)
readPosition += 8
return value
}
actual final override fun writeDouble(v: Double) {
if (writeRemaining < 8) throw IllegalStateException("Not enough space left to write a double")
view.setFloat64(writePosition, v, littleEndian)
writePosition += 8
}
actual final override fun writeFully(src: ByteArray, offset: Int, length: Int) {
if (writeRemaining < length) throw IllegalStateException("Not enough space left ($writeRemaining) to write $length bytes")
val wp = writePosition
val i8 = i8
for (idx in 0 .. length - 1) {
i8[wp + idx] = src[offset + idx]
}
writePosition = wp + length
}
actual final override fun writeFully(src: ShortArray, offset: Int, length: Int) {
if (writeRemaining < length * 2) throw IllegalStateException("Not enough space left to write a short array of length $length")
var wp = writePosition
val platformEndian = platformEndian
if (platformEndian && wp and 1 == 0) {
val array = i16
var j = wp / 2
for (i in offset .. offset + length - 1) {
array[j++] = src[i]
}
writePosition = wp + length * 2
}
else if (platformEndian) {
val array = Int16Array(content, wp)
for (i in offset .. offset + length - 1) {
array[i - offset] = src[i]
}
writePosition = wp + length * 2
} else {
val littleEndian = littleEndian
val view = view
for (i in offset .. offset + length - 1) {
view.setInt16(wp, src[i], littleEndian)
wp += 2
}
writePosition = wp
}
}
actual final override fun writeFully(src: IntArray, offset: Int, length: Int) {
if (writeRemaining < length * 4) throw IllegalStateException("Not enough space left to write an int array of length $length")
var wp = writePosition
val platformEndian = platformEndian
if (platformEndian && wp and 3 == 0) {
val array = i32
var j = wp / 4
for (i in offset .. offset + length - 1) {
array[j++] = src[i]
}
writePosition = wp + length * 4
}
else if (platformEndian) {
val array = Int32Array(content, wp)
for (i in offset .. offset + length - 1) {
array[i - offset] = src[i]
}
writePosition = wp + length * 4
} else {
val littleEndian = littleEndian
val view = view
for (i in offset..offset + length - 1) {
view.setInt32(wp, src[i], littleEndian)
wp += 4
}
writePosition = wp
}
}
actual final override fun writeFully(src: LongArray, offset: Int, length: Int) {
if (writeRemaining < length * 8) throw IllegalStateException("Not enough space left to write a long array of length $length")
for (i in offset .. offset + length - 1) {
writeLong(src[i])
}
}
actual final override fun writeFully(src: FloatArray, offset: Int, length: Int) {
if (writeRemaining < length * 4) throw IllegalStateException("Not enough space left to write a float array of length $length")
var wp = writePosition
val platformEndian = platformEndian
if (platformEndian && wp and 3 == 0) {
val array = f32
var j = wp / 4
for (i in offset .. offset + length - 1) {
array[j++] = src[i]
}
writePosition = wp + length * 4
}
else if (platformEndian) {
val array = Float32Array(content, wp)
for (i in offset .. offset + length - 1) {
array[i - offset] = src[i]
}
writePosition = wp + length * 4
} else {
val littleEndian = littleEndian
val view = view
for (i in offset..offset + length - 1) {
view.setFloat32(wp, src[i], littleEndian)
wp += 4
}
writePosition = wp
}
}
actual final override fun writeFully(src: DoubleArray, offset: Int, length: Int) {
if (writeRemaining < length * 8) throw IllegalStateException("Not enough space left to write a double array of length $length")
var wp = writePosition
val platformEndian = platformEndian
if (platformEndian && wp and 7 == 0) {
val array = f64
var j = wp / 8
for (i in offset .. offset + length - 1) {
array[j++] = src[i]
}
writePosition = wp + length * 8
}
else if (platformEndian) {
val array = Float64Array(content, wp)
for (i in offset .. offset + length - 1) {
array[i - offset] = src[i]
}
writePosition = wp + length * 8
} else {
val littleEndian = littleEndian
val view = view
for (i in offset..offset + length - 1) {
view.setFloat64(wp, src[i], littleEndian)
wp += 8
}
writePosition = wp
}
}
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" }
val otherEnd = src.readPosition + length
val sub = src.i8.subarray(src.readPosition, otherEnd)
i8.set(sub, writePosition)
src.readPosition = otherEnd
writePosition += length
}
actual final override fun fill(n: Long, v: Byte) {
if (writeRemaining.toLong() < n) throw IllegalStateException("Not enough space to fill with $n values")
val wp = writePosition
repeat(n.toInt()) {
i8[wp + it] = v
}
writePosition += n.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) {
if (readRemaining < length) throw IllegalStateException("Not enough bytes available ($readRemaining) to read $length bytes")
val rp = readPosition
val i8 = i8
for (idx in 0 .. length - 1) {
dst[offset + idx] = i8[rp + idx]
}
readPosition += length
}
actual final override fun readAvailable(dst: ByteArray, offset: Int, length: Int): Int {
val readRemaining = readRemaining
if (readRemaining == 0) return -1
val size = minOf(readRemaining, length)
readFully(dst, offset, size)
return size
}
actual final override fun readFully(dst: ShortArray, offset: Int, length: Int) {
if (readRemaining < length * 2) throw IllegalStateException("Not enough bytes available ($readRemaining) to read $length short integers")
var rp = readPosition
val platformEndian = platformEndian
if (platformEndian && rp and 1 == 0) {
val array = i16
var j = rp / 2
for (i in offset..offset + length - 1) {
dst[i] = array[j++]
}
readPosition = rp + length * 2
} else if (platformEndian) {
val array = Int16Array(content, rp)
var j = 0
for (i in offset..offset + length - 1) {
dst[i] = array[j++]
}
readPosition = rp + length * 2
} else {
val littleEndian = littleEndian
val view = view
for (idx in offset..offset + length - 1) {
dst[idx] = view.getInt16(rp, littleEndian)
rp += 2
}
readPosition = rp
}
}
actual final override fun readAvailable(dst: ShortArray, offset: Int, length: Int): Int {
val readRemaining = readRemaining
if (readRemaining == 0) return -1
val size = minOf(readRemaining, length)
readFully(dst, offset, size)
return size
}
actual final override fun readFully(dst: IntArray, offset: Int, length: Int) {
if (readRemaining < length * 4) throw IllegalStateException("Not enough bytes available ($readRemaining) to read $length integers")
var rp = readPosition
val platformEndian = platformEndian
if (platformEndian && rp and 3 == 0) {
val array = i32
var j = rp / 4
for (i in offset..offset + length - 1) {
dst[i] = array[j++]
}
readPosition = rp + length * 4
} else if (platformEndian) {
val array = Int32Array(content, rp)
var j = 0
for (i in offset..offset + length - 1) {
dst[i] = array[j++]
}
readPosition = rp + length * 4
} else {
val littleEndian = littleEndian
val view = view
for (idx in offset..offset + length - 1) {
dst[idx] = view.getInt32(rp, littleEndian)
rp += 4
}
readPosition = rp
}
}
actual final override fun readAvailable(dst: IntArray, offset: Int, length: Int): Int {
val readRemaining = readRemaining
if (readRemaining == 0) return -1
val size = minOf(readRemaining, length)
readFully(dst, offset, size)
return size
}
actual final override fun readFully(dst: LongArray, offset: Int, length: Int) {
if (readRemaining < length * 8) throw IllegalStateException("Not enough bytes available ($readRemaining) to read $length long integers")
for (idx in offset .. offset + length - 1) {
dst[idx] = readLongUnsafe()
}
}
actual final override fun readAvailable(dst: LongArray, offset: Int, length: Int): Int {
val readRemaining = readRemaining
if (readRemaining == 0) return -1
val size = minOf(readRemaining, length)
readFully(dst, offset, size)
return size
}
actual final override fun readFully(dst: FloatArray, offset: Int, length: Int) {
if (readRemaining < length * 4) throw IllegalStateException("Not enough bytes available ($readRemaining) to read $length float numbers")
var rp = readPosition
val platformEndian = platformEndian
if (platformEndian && rp and 3 == 0) {
val array = f32
var j = rp / 4
for (i in offset..offset + length - 1) {
dst[i] = array[j++]
}
readPosition = rp + length * 4
} else if (platformEndian) {
val array = Float32Array(content, rp)
var j = 0
for (i in offset..offset + length - 1) {
dst[i] = array[j++]
}
readPosition = rp + length * 4
} else {
val littleEndian = littleEndian
val view = view
for (idx in offset..offset + length - 1) {
dst[idx] = view.getFloat32(rp, littleEndian)
rp += 4
}
readPosition = rp
}
}
actual final override fun readAvailable(dst: FloatArray, offset: Int, length: Int): Int {
val readRemaining = readRemaining
if (readRemaining == 0) return -1
val size = minOf(readRemaining, length)
readFully(dst, offset, size)
return size
}
actual final override fun readFully(dst: DoubleArray, offset: Int, length: Int) {
if (readRemaining < length * 8) throw IllegalStateException("Not enough bytes available ($readRemaining) to read $length double float numbers")
var rp = readPosition
val platformEndian = platformEndian
if (platformEndian && rp and 7 == 0) {
val array = f64
var j = rp / 8
for (i in offset..offset + length - 1) {
dst[i] = array[j++]
}
readPosition = rp + length * 8
} else if (platformEndian) {
val array = Float64Array(content, rp)
var j = 0
for (i in offset..offset + length - 1) {
dst[i] = array[j++]
}
readPosition = rp + length * 8
} else {
val littleEndian = littleEndian
val view = view
for (idx in offset..offset + length - 1) {
dst[idx] = view.getFloat64(rp, littleEndian)
rp += 8
}
readPosition = rp
}
}
actual final override fun readAvailable(dst: DoubleArray, offset: Int, length: Int): Int {
val readRemaining = readRemaining
if (readRemaining == 0) return -1
val size = minOf(readRemaining, length)
readFully(dst, offset, length)
return size
}
@Deprecated("Use readFully instead", ReplaceWith("readFully(dst, offset, length)"), level = DeprecationLevel.ERROR)
fun read(dst: Array, offset: Int, length: Int) {
return readFully(dst, offset, length)
}
fun readFully(dst: Array, offset: Int, length: Int) {
if (readRemaining < length) throw IllegalStateException("Not enough bytes available ($readRemaining) to read $length bytes")
val rp = readPosition
val i8 = i8
for (idx in 0 .. length - 1) {
dst[offset + idx] = i8[rp + idx]
}
readPosition += length
}
@Deprecated("Use readFully instead", ReplaceWith("readFully(dst, offset, length)"), level = DeprecationLevel.ERROR)
fun read(dst: ArrayBuffer, offset: Int, length: Int) {
readFully(dst, offset, length)
}
final override fun readFully(dst: ArrayBuffer, offset: Int, length: Int) {
if (readRemaining < length) throw IllegalStateException("Not enough bytes available ($readRemaining) to read $length bytes")
val to = Int8Array(dst, offset, length)
val rp = readPosition
val rem = writePosition - rp
val i8 = i8
if (rp == 0 && length == rem) {
to.set(i8, offset)
} else if (length < 100) {
for (i in 0 .. length - 1) {
to[offset + i] = i8[rp + i]
}
} else {
val from = Int8Array(content, rp, length)
to.set(from)
}
readPosition = rp + length
}
override fun readAvailable(dst: ArrayBuffer, offset: Int, length: Int): Int {
val readRemaining = readRemaining
if (readRemaining == 0) return -1
val size = minOf(readRemaining, length)
readFully(dst, offset, size)
return size
}
actual final override fun readAvailable(dst: IoBuffer, length: Int): Int {
val readRemaining = readRemaining
if (readRemaining == 0) return -1
val size = minOf(dst.writeRemaining, readRemaining, length)
readFully(dst, size)
return size
}
override fun readFully(dst: ArrayBufferView, offset: Int, length: Int) {
if (readRemaining < length) throw IllegalStateException("Not enough bytes available ($readRemaining) to read $length bytes")
if (length > dst.byteLength) throw IllegalArgumentException("Destination buffer overflow: length = $length, buffer capacity ${dst.byteLength}")
require(offset >= 0) { "offset should be positive" }
require(offset + length <= dst.byteLength) { throw IndexOutOfBoundsException("") }
readFully(dst.buffer, dst.byteOffset + offset, length)
}
override fun readAvailable(dst: ArrayBufferView, offset: Int, length: Int): Int {
val readRemaining = readRemaining
if (readRemaining == 0) return -1
val size = minOf(length, readRemaining)
readFully(dst, offset, size)
return size
}
@Deprecated("Use readFully instead", ReplaceWith("readFully(dst, offset, length)"), level = DeprecationLevel.ERROR)
fun read(dst: Int8Array, offset: Int, length: Int) {
readFully(dst, offset, length)
}
override fun readFully(dst: Int8Array, offset: Int, length: Int) {
if (readRemaining < length) throw IllegalStateException("Not enough bytes available ($readRemaining) to read $length bytes")
val rp = readPosition
val rem = writePosition - rp
val i8 = i8
if (rp == 0 && rem == length) {
dst.set(i8, offset)
} else if (length < 100) {
for (i in 0 .. length - 1) {
dst[offset + i] = i8[rp + i]
}
} else {
val from = Int8Array(content, rp, length)
dst.set(from, offset)
}
readPosition = rp + length
}
override fun readAvailable(dst: Int8Array, offset: Int, length: Int): Int {
val readRemaining = readRemaining
if (readRemaining == 0) return -1
val size = minOf(readRemaining, length)
readFully(dst, offset, size)
return size
}
actual final override fun readFully(dst: IoBuffer, length: Int) {
if (readRemaining < length) throw IllegalStateException("Not enough bytes available ($readRemaining) to read $length bytes")
if (dst.writeRemaining < length) throw IllegalArgumentException("Not enough free space in dst buffer to write $length bytes")
val srcBuffer = if (i8.length == length) i8 else Int8Array(content, readPosition, length)
dst.i8.set(srcBuffer, dst.writePosition)
dst.writePosition += length
readPosition += length
}
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 = i8.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 = i8
var wp = writePosition
val l = limit
var rc = end
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)
}
@Suppress("NOTHING_TO_INLINE")
private inline fun Int.isSurrogateCodePoint() = this in 55296..57343
private fun appendCharsUtf8(csq: CharArray, start: Int, end: Int, wp0: Int): Int {
val i8 = i8
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 = i8
var wp = writePosition
val l = limit
var rc = end
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 = i8
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 Int8Array.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 Int8Array.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)
}
actual final override fun tryPeek(): Int {
val readPosition = readPosition
val writePosition = writePosition
if (readPosition == writePosition) return -1
return i8[readPosition].toInt() and 0xff
}
@Deprecated("Binary compatibility.", level = DeprecationLevel.HIDDEN)
actual final override fun peekTo(buffer: IoBuffer): Int {
return peekTo(buffer)
}
@Deprecated("Use discardExact instead.")
actual final override fun discard(n: Long): Long {
val size = minOf(readRemaining.toLong(), n).toInt()
readPosition += size
return size.toLong()
}
@Deprecated("Use writeFully instead", level = DeprecationLevel.ERROR)
actual fun write(array: ByteArray, offset: Int, length: Int) {
writeFully(array, offset, length)
}
fun writeFully(src: ArrayBufferView, offset: Int, length: Int) {
if (writeRemaining < length) throw IllegalStateException("Not enough space left ($writeRemaining) to write $length bytes")
val wp = writePosition
val rem = limit - wp
val i8 = i8
if (length > rem) throw IndexOutOfBoundsException()
val from = Int8Array(src.buffer, src.byteOffset + offset, length)
i8.set(from, wp)
writePosition = wp + length
}
fun write(src: Int8Array, offset: Int, length: Int) {
if (writeRemaining < length) throw IllegalStateException("Not enough space left ($writeRemaining) to write $length bytes")
val wp = writePosition
val rem = limit - wp
val i8 = i8
if (length > rem) throw IndexOutOfBoundsException()
if (offset == 0 && length == src.length) {
i8.set(src, wp)
} else if (length < 100) {
for (i in 0 .. length - 1) {
i8[wp + i] = src[offset + i]
}
} else {
val from = Int8Array(src.buffer, src.byteOffset + offset, length)
i8.set(from, wp)
}
writePosition = wp + length
}
actual final override fun readLong(): Long {
if (readRemaining < 8) throw IllegalStateException("Not enough bytes available to read a long")
return readLongUnsafe()
}
private fun readLongUnsafe(): Long {
val m = 0xffffffff
val a = readIntUnsafe().toLong() and m
val b = readIntUnsafe().toLong() and m
return if (littleEndian) {
(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()
if (littleEndian) {
writeInt(b)
writeInt(a)
} else {
writeInt(a)
writeInt(b)
}
}
actual fun discardExact(n: Int) {
require(n >= 0) { "Number of bytes to be discarded shouldn't be negative: $n" }
val rem = readRemaining
if (n > rem) {
throw IllegalArgumentException("Can't discard $n bytes: only $rem bytes available")
}
readPosition += n
}
actual fun pushBack(n: Int) {
if (readPosition < n) throw IllegalStateException("Nothing to push back")
readPosition -= n
}
actual fun resetForWrite() {
resetForWrite(content.byteLength)
}
actual fun resetForWrite(limit: Int) {
require(limit >= 0) { "Limit shouldn't be negative: $limit" }
require(limit <= content.byteLength) { "Limit shouldn't be bigger than buffer size: limit = $limit, size = ${content.byteLength}"}
readPosition = 0
writePosition = 0
this.limit = limit
}
actual fun resetForRead() {
readPosition = 0
limit = content.byteLength
writePosition = limit
}
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, 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)
}
}
}
@Deprecated("Use writeFully instead", ReplaceWith("writeFully(src, length)"), level = DeprecationLevel.ERROR)
actual fun writeBuffer(src: IoBuffer, length: Int): Int {
writeFully(src, length)
return length
}
actual final override fun flush() {
}
internal fun readText(decoder: TextDecoder, out: Appendable, lastBuffer: Boolean, max: Int = Int.MAX_VALUE): Int {
require(max >= 0) { "max shouldn't be negative: $max" }
if (readRemaining == 0) return 0
val rawResult = decoder.decodeStream(i8.subarray(readPosition, writePosition), !lastBuffer)
val result = if (rawResult.length <= max) {
readPosition = writePosition
rawResult
} else {
val actual = rawResult.substring(0, max)
// as js's text decoder is too stupid, let's guess new readPosition
val subDecoder = TextDecoderFatal(decoder.encoding, true)
val subArray = Int8Array(1)
var subDecoded = 0
for (i in readPosition until writePosition) {
subArray[0] = i8[i]
subDecoded += subDecoder.decodeStream(subArray, true).length
if (subDecoded >= max) {
readPosition = i + 1
break
}
}
if (subDecoded < max) {
subDecoded += decodeWrap { subDecoder.decode().length }
if (subDecoded >= max) {
readPosition = writePosition
} else {
throw IllegalStateException("Failed to readText: don't know how to update read position")
}
}
actual
}
out.append(result)
return result.length
}
@PublishedApi
internal fun readableView(): DataView {
val readPosition = readPosition
val writePosition = writePosition
return when {
readPosition == writePosition -> EmptyDataView
readPosition == 0 && writePosition == content.byteLength -> view
else -> DataView(content, readPosition, writePosition - readPosition)
}
}
@PublishedApi
internal fun writableView(): DataView {
val writePosition = writePosition
val limit = limit
return when {
writePosition == limit -> EmptyDataView
writePosition == 0 && limit == content.byteLength -> view
else -> DataView(content, writePosition, limit - writePosition)
}
}
@PublishedApi
internal fun commitWritten(n: Int) {
writePosition += n
}
/**
* Apply [block] function on a [DataView] of readable bytes.
* The [block] function should return number of consumed bytes.
* @return number of bytes consumed
*/
@ExperimentalIoApi
inline fun readDirect(block: (DataView) -> Int): Int {
val view = readableView()
val rc = block(view)
check(rc >= 0) { "The returned value from block function shouldn't be negative: $rc" }
discardExact(rc)
return rc
}
/**
* Apply [block] function on a [DataView] of the free space.
* The [block] function should return number of written bytes.
* @return number of bytes written
*/
@ExperimentalIoApi
inline fun writeDirect(block: (DataView) -> Int): Int {
val view = writableView()
val rc = block(view)
check(rc >= 0) { "The returned value from block function shouldn't be negative: $rc"}
check(rc <= writeRemaining) { "The returned value from block function is too big: $rc > $writeRemaining" }
commitWritten(rc)
return rc
}
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 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 writeBufferPrepend(other: IoBuffer) {
val size = other.readRemaining
require(size <= startGap) { "size should be greater than startGap (size = $size, startGap = $startGap)" }
val otherEnd = other.readPosition + size
val sub = other.i8.subarray(other.readPosition, otherEnd)
i8.set(sub, readPosition - size)
readPosition -= size
other.readPosition += size
}
internal actual fun writeBufferAppend(other: IoBuffer, maxSize: Int) {
val size = minOf(other.readRemaining, maxSize)
require(size <= writeRemaining + endGap) { "should be greater than write space + end gap (size = $size, " +
"writeRemaining = $writeRemaining, endGap = $endGap, rem+gap = ${writeRemaining + endGap}" }
val otherEnd = other.readPosition + size
val sub = other.i8.subarray(other.readPosition, otherEnd)
i8.set(sub, writePosition)
writePosition += size
if (writePosition > limit) {
limit = writePosition
}
other.readPosition += size
}
internal fun unlink() {
if (refCount != 0) throw IllegalStateException("Unable to unlink buffers: buffer view is in use")
content = EmptyBuffer
i8 = Empty8
i16 = Empty16
i32 = Empty32
f32 = EmptyF32
f64 = EmptyF64
view = EmptyDataView
resetForWrite()
}
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
}
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
private val EmptyBuffer = ArrayBuffer(0)
private val EmptyDataView = DataView(EmptyBuffer)
private val Empty8 = Int8Array(0)
private val Empty16 = Int16Array(0)
private val Empty32 = Int32Array(0)
private val EmptyF32 = Float32Array(0)
private val EmptyF64 = Float64Array(0)
actual val Empty = IoBuffer(EmptyBuffer, null)
actual val Pool: ObjectPool = object: DefaultPool(BUFFER_VIEW_POOL_SIZE) {
override fun produceInstance(): IoBuffer {
return IoBuffer(ArrayBuffer(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) {
instance.unlink()
}
}
actual val NoPool: ObjectPool = object : NoPoolImpl() {
override fun borrow(): IoBuffer {
return IoBuffer(ArrayBuffer(4096), null)
}
}
actual val EmptyPool: ObjectPool = EmptyBufferPoolImpl
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy