support for fixed size ints (all), but only BE

This commit is contained in:
Sergey Chernov 2023-04-01 17:17:42 +01:00
parent 86eb5e9d7f
commit 641188e92b
5 changed files with 102 additions and 24 deletions

View File

@ -101,3 +101,26 @@ This __field annontation__ allows to store __integer fields__ of any size more c
## @FixedSize(size) ## @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())
~~~

View File

@ -35,7 +35,9 @@ class BipackDecoder(
if (fixedNumber) input.readI32() if (fixedNumber) input.readI32()
else if (nextIsUnsigned) input.readNumber<UInt>().toInt() else input.readNumber() 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 decodeFloat(): Float = input.readFloat()
override fun decodeDouble(): Double = input.readDouble() override fun decodeDouble(): Double = input.readDouble()
override fun decodeChar(): Char = Char(input.readNumber<UInt>().toInt()) override fun decodeChar(): Char = Char(input.readNumber<UInt>().toInt())

View File

@ -44,7 +44,10 @@ class BipackEncoder(val output: DataSink) : AbstractEncoder() {
else output.writeNumber(value) else output.writeNumber(value)
fun encodeUInt(value: UInt) = output.writeNumber(value) fun encodeUInt(value: UInt) = output.writeNumber(value)
override fun encodeLong(value: Long) = if (nextIsUnsigned) override fun encodeLong(value: Long) =
if (fixedNumber)
output.writeI64(value)
else if (nextIsUnsigned)
output.writeNumber(value.toULong()) output.writeNumber(value.toULong())
else else
output.writeNumber(value) output.writeNumber(value)

View File

@ -48,16 +48,47 @@ annotation class CrcProtected
annotation class Unsigned annotation class Unsigned
/** /**
* Use it with collection of fixed size, like hash digest, key bits and so on, by not storing collection * Fixed size collection of a given size. __Use it only with collections!__
* size. It effectively reduced packed size to at least one byte. depending on the actual *
* 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. * 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 * 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 @SerialInfo
@Target(AnnotationTarget.FIELD, AnnotationTarget.PROPERTY) @Target(AnnotationTarget.FIELD, AnnotationTarget.PROPERTY)
annotation class FixedSize(val size: Int) 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 @SerialInfo
@Target(AnnotationTarget.FIELD, AnnotationTarget.PROPERTY) @Target(AnnotationTarget.FIELD, AnnotationTarget.PROPERTY)
annotation class Fixed annotation class Fixed

View File

@ -223,7 +223,7 @@ class BipackEncoderTest {
@Serializable @Serializable
data class Fixa( data class Fixa(
@FixedSize(5) @FixedSize(5)
val x: ByteArray val x: ByteArray,
) )
@Test @Test
@ -307,8 +307,16 @@ class BipackEncoderTest {
@Serializable @Serializable
data class FI16(@Fixed val i: Short) data class FI16(@Fixed val i: Short)
@Serializable
data class FI64(@Fixed val i: Long)
@Serializable @Serializable
data class FU16(@Fixed val i: UShort) data class FU16(@Fixed val i: UShort)
@Serializable
class Foo(
@Fixed
val eightBytesLongInt: Long,
)
@Test @Test
fun testFixedInt() { fun testFixedInt() {
@ -319,7 +327,18 @@ class BipackEncoderTest {
assertEquals(-1, BipackEncoder.encode(FI32(-1)).decodeFromBipack<FI32>().i) assertEquals(-1, BipackEncoder.encode(FI32(-1)).decodeFromBipack<FI32>().i)
assertEquals("FF FF FF FF", BipackEncoder.encode(FU32(0xFFFFFFFFu)).encodeToHex()) assertEquals("FF FF FF FF", BipackEncoder.encode(FU32(0xFFFFFFFFu)).encodeToHex())
assertEquals("FF 01 02 03", BipackEncoder.encode(FU32(0xFF010203u)).encodeToHex()) assertEquals("FF 01 02 03", BipackEncoder.encode(FU32(0xFF010203u)).encodeToHex())
assertEquals("FF 03", BipackEncoder.encode(FU16(0xFF03u)).encodeToHex()) assertEquals("FF 03", BipackEncoder.encode(FU16(0xFF03u)).encodeToHex())
assertEquals(0x7ffeu, BipackEncoder.encode(FU16(0x7ffeu)).decodeFromBipack<FU16>().i) 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())
} }
} }