diff --git a/build.gradle.kts b/build.gradle.kts index 1fe81a3..a3af6b5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -20,9 +20,15 @@ val projectGroup: String by project val projectVersion: String by project val specifyKotlinAsDependency: String by project val fastutilVersion: String by project +val jupiterVersion: String by project + +tasks.test { + useJUnitPlatform() +} dependencies { testImplementation("org.jetbrains.kotlin:kotlin-test") + testImplementation("org.junit.jupiter:junit-jupiter:${jupiterVersion}") implementation("it.unimi.dsi:fastutil:$fastutilVersion") } diff --git a/gradle.properties b/gradle.properties index 30fcabd..2c5bdab 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,8 +4,9 @@ kotlin.code.style=official specifyKotlinAsDependency=false projectGroup=ru.dbotthepony.kommons -projectVersion=2.1.7 +projectVersion=2.1.8 guavaDepVersion=33.0.0 gsonDepVersion=2.8.9 fastutilVersion=8.5.6 +jupiterVersion=5.9.2 diff --git a/src/main/kotlin/ru/dbotthepony/kommons/io/InputStreamUtils.kt b/src/main/kotlin/ru/dbotthepony/kommons/io/InputStreamUtils.kt index c232d21..b8de787 100644 --- a/src/main/kotlin/ru/dbotthepony/kommons/io/InputStreamUtils.kt +++ b/src/main/kotlin/ru/dbotthepony/kommons/io/InputStreamUtils.kt @@ -89,7 +89,7 @@ private fun readVarLongInfo(supplier: IntSupplier): VarLongReadResult { throw EOFException() while (true) { - result = (result shl 7) or (read.toLong() and 0x7F) + result = (result shl 7) or (read.toLong() and 0x7FL) if (read and 0x80 == 0) break @@ -154,22 +154,85 @@ private fun VarLongReadResult.fromSignedVar(): VarLongReadResult { return VarLongReadResult(-(this.value ushr 1) - 1, this.cells) } +/** + * Reads unsigned variable length integer, with Big-endian byte order + */ fun RandomAccessFile.readVarLong() = readVarLong(::readUnsignedByte) + +/** + * Reads unsigned variable length integer, with Big-endian byte order + */ fun RandomAccessFile.readVarInt() = readVarInt(::readUnsignedByte) + +/** + * Reads unsigned variable length integer, with Big-endian byte order + */ fun RandomAccessFile.readVarLongInfo() = readVarLongInfo(::readUnsignedByte) + +/** + * Reads unsigned variable length integer, with Big-endian byte order + */ fun RandomAccessFile.readVarIntInfo() = readVarIntInfo(::readUnsignedByte) + +/** + * Reads unsigned variable length integer, with Big-endian byte order + */ fun InputStream.readVarLong() = readVarLong(::read) + +/** + * Reads unsigned variable length integer, with Big-endian byte order + */ fun InputStream.readVarInt() = readVarInt(::read) + +/** + * Reads unsigned variable length integer, with Big-endian byte order + */ fun InputStream.readVarLongInfo() = readVarLongInfo(::read) + +/** + * Reads unsigned variable length integer, with Big-endian byte order + */ fun InputStream.readVarIntInfo() = readVarIntInfo(::read) + +/** + * Reads signed variable length integer, with Big-endian byte order + */ fun RandomAccessFile.readSignedVarLong() = readVarLong(::readUnsignedByte).fromSignedVar() + +/** + * Reads signed variable length integer, with Big-endian byte order + */ fun RandomAccessFile.readSignedVarInt() = readVarInt(::readUnsignedByte).fromSignedVar() + +/** + * Reads signed variable length integer, with Big-endian byte order + */ fun RandomAccessFile.readSignedVarLongInfo() = readVarLongInfo(::readUnsignedByte).fromSignedVar() + +/** + * Reads signed variable length integer, with Big-endian byte order + */ fun RandomAccessFile.readSignedVarIntInfo() = readVarIntInfo(::readUnsignedByte).fromSignedVar() + +/** + * Reads signed variable length integer, with Big-endian byte order + */ fun InputStream.readSignedVarLong() = readVarLong(::read).fromSignedVar() + +/** + * Reads signed variable length integer, with Big-endian byte order + */ fun InputStream.readSignedVarInt() = readVarInt(::read).fromSignedVar() + +/** + * Reads signed variable length integer, with Big-endian byte order + */ fun InputStream.readSignedVarLongInfo() = readVarLongInfo(::read).fromSignedVar() + +/** + * Reads signed variable length integer, with Big-endian byte order + */ fun InputStream.readSignedVarIntInfo() = readVarIntInfo(::read).fromSignedVar() fun InputStream.readBinaryString(): String { diff --git a/src/main/kotlin/ru/dbotthepony/kommons/io/OutputStreamUtils.kt b/src/main/kotlin/ru/dbotthepony/kommons/io/OutputStreamUtils.kt index b7dbb14..1f5cf48 100644 --- a/src/main/kotlin/ru/dbotthepony/kommons/io/OutputStreamUtils.kt +++ b/src/main/kotlin/ru/dbotthepony/kommons/io/OutputStreamUtils.kt @@ -83,22 +83,25 @@ fun OutputStream.writeFloat(value: Float) = writeInt(value.toBits()) fun OutputStream.writeDouble(value: Double) = writeLong(value.toBits()) private fun writeVarLong(write: IntConsumer, value: Long): Int { - var value = value - var i = 0 + var length = 9 - do { - val toWrite = (value and 0x7F).toInt() - value = value ushr 7 + while (length > 0) { + val octetsPresent = (value ushr (length * 7)) and 0x7FL - if (value == 0L) - write.accept(toWrite) - else - write.accept(toWrite or 0x80) + if (octetsPresent != 0L) { + break + } else { + length-- + } + } - i++ - } while (value != 0L) + for (octet in length downTo 1) { + write.accept(((value ushr (octet * 7)) and 0x7FL).toInt() or 0x80) + } - return i + write.accept((value and 0x7FL).toInt()) + + return length + 1 } private fun writeVarInt(write: IntConsumer, value: Int): Int { @@ -119,14 +122,45 @@ private fun Long.toSignedVar(): Long { return ((-this - 1) shl 1) or 1L } +/** + * Writes unsigned variable length integer, with Big-endian byte order + */ fun RandomAccessFile.writeVarLong(value: Long) = writeVarLong(::write, value) + +/** + * Writes unsigned variable length integer, with Big-endian byte order + */ fun RandomAccessFile.writeVarInt(value: Int) = writeVarInt(::write, value) + +/** + * Writes unsigned variable length integer, with Big-endian byte order + */ fun OutputStream.writeVarLong(value: Long) = writeVarLong(::write, value) + +/** + * Writes unsigned variable length integer, with Big-endian byte order + */ fun OutputStream.writeVarInt(value: Int) = writeVarInt(::write, value) + +/** + * Writes signed variable length integer, with Big-endian byte order + */ fun RandomAccessFile.writeSignedVarLong(value: Long) = writeVarLong(::write, value.toSignedVar()) + +/** + * Writes signed variable length integer, with Big-endian byte order + */ fun RandomAccessFile.writeSignedVarInt(value: Int) = writeVarInt(::write, value.toSignedVar()) + +/** + * Writes signed variable length integer, with Big-endian byte order + */ fun OutputStream.writeSignedVarLong(value: Long) = writeVarLong(::write, value.toSignedVar()) + +/** + * Writes signed variable length integer, with Big-endian byte order + */ fun OutputStream.writeSignedVarInt(value: Int) = writeVarInt(::write, value.toSignedVar()) fun OutputStream.writeBinaryString(input: String) { diff --git a/src/test/kotlin/ru/dbotthepony/kommons/test/StreamTests.kt b/src/test/kotlin/ru/dbotthepony/kommons/test/StreamTests.kt new file mode 100644 index 0000000..cb7bccb --- /dev/null +++ b/src/test/kotlin/ru/dbotthepony/kommons/test/StreamTests.kt @@ -0,0 +1,38 @@ +package ru.dbotthepony.kommons.test + +import it.unimi.dsi.fastutil.io.FastByteArrayInputStream +import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream +import org.junit.jupiter.api.Test +import ru.dbotthepony.kommons.io.readVarInt +import ru.dbotthepony.kommons.io.readVarIntInfo +import ru.dbotthepony.kommons.io.writeVarInt +import kotlin.test.assertEquals + +object StreamTests { + private fun readWrite(value: Int) { + val output = FastByteArrayOutputStream() + val input = FastByteArrayInputStream(output.array, 0, output.length) + val r = input.readVarIntInfo() + assertEquals(value, r.value) + } + + @Test + fun varInts() { + readWrite(0) + readWrite(1) + readWrite(2) + readWrite(3) + readWrite(63) + readWrite(64) + readWrite(65) + readWrite(120) + readWrite(121) + readWrite(126) + readWrite(127) + readWrite(128) + readWrite(129) + readWrite(254) + readWrite(255) + readWrite(256) + } +} \ No newline at end of file