support for fixed size ints (all), but only BE
This commit is contained in:
parent
86eb5e9d7f
commit
641188e92b
25
README.md
25
README.md
@ -100,4 +100,27 @@ This __field annontation__ allows to store __integer fields__ of any size more c
|
||||
|
||||
## @FixedSize(size)
|
||||
|
||||
Use it with fixed-size collections (like hashes, keys, etc) to not to keep collection size in the packed binary. It saves at least one byte.
|
||||
Use it with fixed-size collections (like hashes, keys, etc) to not to keep collection size in the packed binary. It saves at least one byte.
|
||||
|
||||
## @Fixed
|
||||
|
||||
Can be used with any integer type to store/restor it as is, fixed-size, big-endian:
|
||||
|
||||
- Short, UShort: 2 bytes
|
||||
- Int, UInt: 4 bytes
|
||||
- Long, ULong: 8 bytes
|
||||
|
||||
Note that without this modifier all integers are serialized into variable-length compressed format, see class [Smartint] from this library.
|
||||
|
||||
Example:
|
||||
~~~kotlin
|
||||
@Serializable
|
||||
class Foo(
|
||||
@Fixed
|
||||
val eightBytesLongInt: Long
|
||||
)
|
||||
|
||||
// so:
|
||||
assertEquals("00 00 00 01 00 00 00 02", BipackEncoder.encode(Foo(0x100000002)).encodeToHex())
|
||||
~~~
|
||||
|
||||
|
@ -35,7 +35,9 @@ class BipackDecoder(
|
||||
if (fixedNumber) input.readI32()
|
||||
else if (nextIsUnsigned) input.readNumber<UInt>().toInt() else input.readNumber()
|
||||
|
||||
override fun decodeLong(): Long = if (nextIsUnsigned) input.readNumber<ULong>().toLong() else input.readNumber()
|
||||
override fun decodeLong(): Long =
|
||||
if( fixedNumber ) input.readI64()
|
||||
else if (nextIsUnsigned) input.readNumber<ULong>().toLong() else input.readNumber()
|
||||
override fun decodeFloat(): Float = input.readFloat()
|
||||
override fun decodeDouble(): Double = input.readDouble()
|
||||
override fun decodeChar(): Char = Char(input.readNumber<UInt>().toInt())
|
||||
|
@ -44,10 +44,13 @@ class BipackEncoder(val output: DataSink) : AbstractEncoder() {
|
||||
else output.writeNumber(value)
|
||||
|
||||
fun encodeUInt(value: UInt) = output.writeNumber(value)
|
||||
override fun encodeLong(value: Long) = if (nextIsUnsigned)
|
||||
output.writeNumber(value.toULong())
|
||||
else
|
||||
output.writeNumber(value)
|
||||
override fun encodeLong(value: Long) =
|
||||
if (fixedNumber)
|
||||
output.writeI64(value)
|
||||
else if (nextIsUnsigned)
|
||||
output.writeNumber(value.toULong())
|
||||
else
|
||||
output.writeNumber(value)
|
||||
|
||||
override fun encodeFloat(value: Float) = output.writeFloat(value)
|
||||
override fun encodeDouble(value: Double) = output.writeDouble(value)
|
||||
|
@ -48,16 +48,47 @@ annotation class CrcProtected
|
||||
annotation class Unsigned
|
||||
|
||||
/**
|
||||
* Use it with collection of fixed size, like hash digest, key bits and so on, by not storing collection
|
||||
* size. It effectively reduced packed size to at least one byte. depending on the actual
|
||||
* Fixed size collection of a given size. __Use it only with collections!__
|
||||
*
|
||||
* Use it with collection of fixed size, to read/write exact number of items (for example, bytes),
|
||||
* like hash digest, key bits and so on. Does not stores/loades the collection
|
||||
* size what reduces packed size to at least one byte. depending on the actual
|
||||
* collection size. As for nowonly collection types (e.g. ByteArray, List<T>m etc) are supported.
|
||||
* Note that if the actual collection size differs from [size], [BipackEncoder] will throw
|
||||
* [WrongCollectionSize] while encoding it.
|
||||
* [WrongCollectionSize] while encoding it. For example:
|
||||
*
|
||||
* ~~~
|
||||
* @Serializable
|
||||
* class Foo(
|
||||
* @FixedSize(32)
|
||||
* thirtyTwoBytes: ByteArray
|
||||
* )
|
||||
*/
|
||||
@SerialInfo
|
||||
@Target(AnnotationTarget.FIELD, AnnotationTarget.PROPERTY)
|
||||
annotation class FixedSize(val size: Int)
|
||||
|
||||
/**
|
||||
* Fixed-size number, big-endian. Could be used only with field of following types:
|
||||
*
|
||||
* - Int, UInt: 4 bytes
|
||||
* - Short, UShort: 2 bytes
|
||||
* - Long, ULong: 8 bytes.
|
||||
*
|
||||
* It should not be used with Byte or UByte as their size is always 1 byte ;)
|
||||
*
|
||||
* Example:
|
||||
* ~~~
|
||||
* @Serializable
|
||||
* class Foo(
|
||||
* @Fixed
|
||||
* val eightBytesLongInt: Long
|
||||
* )
|
||||
*
|
||||
* // so:
|
||||
* assertEquals("00 00 00 01 00 00 00 02", BipackEncoder.encode(Foo(0x100000002)).encodeToHex())
|
||||
* ~~~
|
||||
*/
|
||||
@SerialInfo
|
||||
@Target(AnnotationTarget.FIELD, AnnotationTarget.PROPERTY)
|
||||
annotation class Fixed
|
||||
|
@ -58,7 +58,7 @@ class BipackEncoderTest {
|
||||
|
||||
@Test
|
||||
fun encodeSimple() {
|
||||
val a = Foobar1N(1,2)//, "bum")
|
||||
val a = Foobar1N(1, 2)//, "bum")
|
||||
println(BipackEncoder.encode(a).toDump())
|
||||
assertEquals(2, BipackEncoder.encode(a).size)
|
||||
val b = BipackDecoder.decode<Foobar1N>(BipackEncoder.encode(a))
|
||||
@ -153,11 +153,11 @@ class BipackEncoderTest {
|
||||
@Serializable
|
||||
data class Types1(
|
||||
val i: Int = 7,
|
||||
val f: Float = 1f/3f,
|
||||
val d: Double = 1.0/3.0,
|
||||
val f: Float = 1f / 3f,
|
||||
val d: Double = 1.0 / 3.0,
|
||||
val b: Boolean = true,
|
||||
val s: String = "жёпа",
|
||||
val ba: ByteArray = byteArrayOf(1,2,3),
|
||||
val ba: ByteArray = byteArrayOf(1, 2, 3),
|
||||
val ch: Char = 'Ы',
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
@ -209,7 +209,7 @@ class BipackEncoderTest {
|
||||
// less significant part (it actually uses native JS number which is longer than Float)
|
||||
// So for the test purposes we need exact values (that packs into float exacly, like nitegers
|
||||
// and some decimals with finite and short binary representation):
|
||||
val t1 = Types1(f=0.5f)
|
||||
val t1 = Types1(f = 0.5f)
|
||||
val d = BipackEncoder.encode(t1)
|
||||
println(d.toDump())
|
||||
val t2: Types1 = d.decodeFromBipack()
|
||||
@ -223,14 +223,14 @@ class BipackEncoderTest {
|
||||
@Serializable
|
||||
data class Fixa(
|
||||
@FixedSize(5)
|
||||
val x: ByteArray
|
||||
val x: ByteArray,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun textFixed() {
|
||||
val x = byteArrayOf(1,2,3,4,5)
|
||||
val x = byteArrayOf(1, 2, 3, 4, 5)
|
||||
//@Fixed(32)
|
||||
val y = Fixa(byteArrayOf(1,2,3,4,5))
|
||||
val y = Fixa(byteArrayOf(1, 2, 3, 4, 5))
|
||||
val d1 = BipackEncoder.encode(x)
|
||||
println(d1.toDump())
|
||||
assertEquals(6, d1.size)
|
||||
@ -241,14 +241,14 @@ class BipackEncoderTest {
|
||||
assertContentEquals(x, BipackDecoder.decode<Fixa>(d2).x)
|
||||
}
|
||||
|
||||
// @Test
|
||||
// @Test
|
||||
@Test
|
||||
fun testMaps() {
|
||||
val t1 = mapOf("foo bar" to "f1", "bar" to "b1", "bazz" to "b3")//f=17f/7f)
|
||||
val d = BipackEncoder.encode(t1)
|
||||
println(d.toDump())
|
||||
println(t1)
|
||||
println(BipackDecoder.decode<Map<String,String>>(d))
|
||||
println(BipackDecoder.decode<Map<String, String>>(d))
|
||||
// println(d.decodeFromBipack<Map<String,String>>())
|
||||
// assertEquals(t1, d.decodeFromBipack())
|
||||
}
|
||||
@ -260,7 +260,7 @@ class BipackEncoderTest {
|
||||
val d = BipackEncoder.encode(t1)
|
||||
println(d.toDump())
|
||||
println(t1)
|
||||
println(BipackDecoder.decode<Map<String,Foobar1N>>(d))
|
||||
println(BipackDecoder.decode<Map<String, Foobar1N>>(d))
|
||||
// println(d.decodeFromBipack<Map<String,String>>())
|
||||
// assertEquals(t1, d.decodeFromBipack())
|
||||
}
|
||||
@ -274,7 +274,7 @@ class BipackEncoderTest {
|
||||
val d = BipackEncoder.encode(t1)
|
||||
println(d.toDump())
|
||||
println(t1)
|
||||
println(BipackDecoder.decode<Map<String,Foobar1>>(d))
|
||||
println(BipackDecoder.decode<Map<String, Foobar1>>(d))
|
||||
// println(d.decodeFromBipack<Map<String,String>>())
|
||||
// assertEquals(t1, d.decodeFromBipack())
|
||||
}
|
||||
@ -288,7 +288,7 @@ class BipackEncoderTest {
|
||||
@Serializable
|
||||
// @Framed
|
||||
@CrcProtected
|
||||
data class Outer(val i1: Inner,val i2: Inner)
|
||||
data class Outer(val i1: Inner, val i2: Inner)
|
||||
|
||||
@Test
|
||||
fun testNestedInOuterProtected() {
|
||||
@ -307,19 +307,38 @@ class BipackEncoderTest {
|
||||
@Serializable
|
||||
data class FI16(@Fixed val i: Short)
|
||||
|
||||
@Serializable
|
||||
data class FI64(@Fixed val i: Long)
|
||||
|
||||
@Serializable
|
||||
data class FU16(@Fixed val i: UShort)
|
||||
@Serializable
|
||||
class Foo(
|
||||
@Fixed
|
||||
val eightBytesLongInt: Long,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun testFixedInt() {
|
||||
val a= FI32(127)
|
||||
val a = FI32(127)
|
||||
println(BipackEncoder.encode(a).toDump())
|
||||
assertContentEquals(byteArrayOf(0,0,0,0x7f), BipackEncoder.encode(a))
|
||||
assertContentEquals(byteArrayOf(0, 0, 0, 0x7f), BipackEncoder.encode(a))
|
||||
assertEquals("FF FF FF FF", BipackEncoder.encode(FI32(-1)).encodeToHex())
|
||||
assertEquals(-1, BipackEncoder.encode(FI32(-1)).decodeFromBipack<FI32>().i)
|
||||
assertEquals("FF FF FF FF", BipackEncoder.encode(FU32(0xFFFFFFFFu)).encodeToHex())
|
||||
assertEquals("FF 01 02 03", BipackEncoder.encode(FU32(0xFF010203u)).encodeToHex())
|
||||
|
||||
assertEquals("FF 03", BipackEncoder.encode(FU16(0xFF03u)).encodeToHex())
|
||||
assertEquals(0x7ffeu, BipackEncoder.encode(FU16(0x7ffeu)).decodeFromBipack<FU16>().i)
|
||||
assertEquals(-1, BipackEncoder.encode(FI16(-1)).decodeFromBipack<FI16>().i)
|
||||
assertEquals(0x7ffe, BipackEncoder.encode(FI16(0x7ffe)).decodeFromBipack<FI16>().i)
|
||||
|
||||
assertEquals("00 00 00 01 00 00 00 02", BipackEncoder.encode(FI64(0x100000002)).encodeToHex())
|
||||
assertEquals(0x100000002, BipackEncoder.encode(FI64(0x100000002)).decodeFromBipack<FI64>().i)
|
||||
|
||||
|
||||
// so:
|
||||
assertEquals("00 00 00 01 00 00 00 02", BipackEncoder.encode(Foo(0x100000002)).encodeToHex())
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user