diff --git a/README.md b/README.md index b9a7556..978a4ea 100644 --- a/README.md +++ b/README.md @@ -14,4 +14,4 @@ Goals: ## SmartInt codec -Variable-length signed and unsigned integer codec, see `object SmartInt`. For not too small numbers it is slightly more effective than `VarInt` codec, for example on `Long` values it saves a byte. \ No newline at end of file +Variable-length signed and unsigned integer codec, see [src:Smartint]. For not too small numbers it is slightly more effective than `VarInt` codec, for example on `Long` values it saves a byte. \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 6f4a5a8..d53e8ca 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - kotlin("multiplatform") version "1.7.21" + kotlin("multiplatform") version "1.8.10" } group = "net.sergeych" @@ -23,9 +23,9 @@ kotlin { } js(IR) { browser { - commonWebpackConfig { - cssSupport.enabled = true - } +// commonWebpackConfig { +// cssSupport.enabled = true +// } } } val hostOs = System.getProperty("os.name") @@ -42,7 +42,7 @@ kotlin { val commonMain by getting { dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4") - api("net.sergeych:mp_stools:1.3.2") + api("net.sergeych:mp_stools:[1.3.3,)") } } val commonTest by getting { diff --git a/src/commonMain/kotlin/net.sergeych.bintools/smartint.kt b/src/commonMain/kotlin/net.sergeych.bintools/smartint.kt index 45d675c..424672e 100644 --- a/src/commonMain/kotlin/net.sergeych.bintools/smartint.kt +++ b/src/commonMain/kotlin/net.sergeych.bintools/smartint.kt @@ -21,31 +21,50 @@ import net.sergeych.bintools.* * | 10 | 64 | --- | * * In other words, except for very small numbers smartint - * gives 1 bit gain. So, full sized 64 bits with smartint takes - * 9 bytes, while varint needs 10. This could be important. + * gives 1 data bit gain for the same packed byte size. For example, + * full size 64 bits number with smartint takes one byte less (9 bytes vs. 10 in Varint). * - * Encoding is the following: + * So, except for values in range 32..63 it gives same or better byte size effectiveness + * than `Varint`. In particular: * - * Byte 0: bits 0..1 : type - * bits 2..7 : v0 + * The effect of it could be interpreted as: * - * Then depending on the type: + * | number values | size | + * |:--------------|:------:| + * | 0..31 | same | + * | 32..63 | worse 1 byte | + * | 64..1048573 | same | + * | 1048576..2097151 | 1 byte better | + * | 2097152..134217727 | same | + * | 134217728..268435456 | 1 byte better | * - * type = 0: - * v0 is the resul 0..64 (or -32..32) + * etc. * - * type = 1: - * v0, v1 is the result, 14 bits + * ## Encoding format * - * type = 2: - * v0, v1, v2 are the result, 22bits + * Enncoded data could be 1 or more bytes in length. Data are + * packed as follows: * - * type = 3: - * v0, v1, v2, varint encoded + * | byte offset | bits range | field | + * |-------------|------------|-------| + * | 0 | 0..1 | type | + * | 0 | 2..7 | v0 | + * | 1 | 0..7 | v1 (when used) | + * | 2 | 0..7 | v2 (when used) | * - * Varint encodes bytes with last bit reserved as end - * flag and first 7 bits are data bits. Last bit 0 means end of the - * sequence. + * Then depending on the `type` field: + * + * | type | encoded | + * |------|---------| + * | 0 | v0 is the result 0..64 (or -32..32) | + * | 1 | v0 ## v1 are the result, 14 bits | + * | 2 | v0 ## v1 ## v2 are the result, 22bits + * | 3 | v0, ## v1 ## v2 ## (varint encoded rest) | + * + * Where `##` means bits concatenation. The bits are interpreted as BIG ENDIAN, + * for example `24573` will be encoded to `EA FF 02` + * + * See also [Varint] for its encoding description. * */ object Smartint : IntCodec { diff --git a/src/commonTest/kotlin/bintools/SmartintTest.kt b/src/commonTest/kotlin/bintools/SmartintTest.kt index 85c870a..5f570ae 100644 --- a/src/commonTest/kotlin/bintools/SmartintTest.kt +++ b/src/commonTest/kotlin/bintools/SmartintTest.kt @@ -10,7 +10,7 @@ import kotlin.test.assertEquals class SmartintTest { - inline fun testValue(x: T) { + inline fun testValue(x: T) { assertEquals(x, Smartint.decode(Smartint.encode(x))) println("+ ${x}: ${Smartint.encode(x).encodeToHex()}") } @@ -19,12 +19,12 @@ class SmartintTest { println("----- $bits -----") val window = 5 var median: Long = (1.toULong() shl bits).toLong() - for( x in (median-2)..(median+2)) { + for (x in (median - 2)..(median + 2)) { testValue(x) testValue(-x) } median = median * 3 / 2 - for( x in (median-5)..(median+5)) { + for (x in (median - 5)..(median + 5)) { testValue(x) testValue(-x) } @@ -32,7 +32,7 @@ class SmartintTest { @Test fun encode() { - for( i in 0..70) testValue(i.toLong()) + for (i in 0..70) testValue(i.toLong()) testAround(6) testAround(14) testAround(22) @@ -42,14 +42,30 @@ class SmartintTest { // testAround(28) } - @Test +// @Test fun compareTest() { - for( x in listOf(0uL, 1uL, 66uL, 129uL, 219uL, 0x1122uL, 0xFFEEuL, 0xAAbbCCdduL, - 0x1111222233334444uL, (1UL shl 63))) { +// for( x in listOf(0uL, 1uL, 66uL, 129uL, 219uL, 0x1122uL, 0xFFEEuL, 0xAAbbCCdduL, +// 0x1111222233334444uL, (1UL shl 63))) { + var lastDelta = 0 + for (x in 0..3000000000) { + val n1 = Varint.encode(x).size + val n2 = Smartint.encode(x).size + val delta = n1 - n2 + if (delta != lastDelta) { + when { + delta < 0 -> println("LOSS: $delta from $x") + delta > 0 -> println("GAIN: $delta from $x") + delta == 0 -> println("SAME: from $x") + } + lastDelta = delta + } +// for( x in listOf(0uL, 1uL, (1UL shl 17)+11UL)) { +// 0x1111222233334444uL, (1UL shl 63))) { // 1--12--23--34--4 - println("--- $x / 0x${x.encodeToHex(8)}") - println("V: ${Varint.encode(x).encodeToHex()}") - println("S: ${Smartint.encode(x).encodeToHex()}") +// println("--- $x / 0x${x.encodeToHex(8)}") +// println("V: ${Varint.encode(x).encodeToHex()}") +// println("S: ${Smartint.encode(x).encodeToHex()}") +// } } } } \ No newline at end of file