support for @Unsigned fields and fox bad error of kotlinjs x is Float
glitch: it returns true for integers
This commit is contained in:
parent
6bc01e9534
commit
0e8f3daf99
@ -25,11 +25,7 @@ interface DataSink {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline fun <reified T:Any>DataSink.writeNumber(value: T) {
|
inline fun <reified T:Any>DataSink.writeNumber(value: T) {
|
||||||
when(value) {
|
Smartint.encode(value, this)
|
||||||
is Float -> writeFloat(value)
|
|
||||||
is Double -> writeDouble(value)
|
|
||||||
else -> Smartint.encode(value, this)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun DataSink.writeI32(value: Int) {
|
fun DataSink.writeI32(value: Int) {
|
||||||
|
@ -13,7 +13,7 @@ interface DataSource {
|
|||||||
/**
|
/**
|
||||||
* Exception that implementations must throw on end of data
|
* Exception that implementations must throw on end of data
|
||||||
*/
|
*/
|
||||||
class EndOfData() : Exception("no more data available")
|
class EndOfData: Exception("no more data available")
|
||||||
|
|
||||||
fun readByte(): Byte
|
fun readByte(): Byte
|
||||||
|
|
||||||
@ -51,3 +51,4 @@ inline fun <reified T : Any> DataSource.readNumber(): T = when(typeOf<T>()) {
|
|||||||
typeOf<Float>() -> readFloat() as T
|
typeOf<Float>() -> readFloat() as T
|
||||||
else -> Smartint.decode(this)
|
else -> Smartint.decode(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,12 +17,14 @@ import net.sergeych.bintools.*
|
|||||||
class BipackDecoder(val input: DataSource, var elementsCount: Int = 0) : AbstractDecoder() {
|
class BipackDecoder(val input: DataSource, var elementsCount: Int = 0) : AbstractDecoder() {
|
||||||
private var elementIndex = 0
|
private var elementIndex = 0
|
||||||
|
|
||||||
|
private var nextIsUnsigned = false
|
||||||
|
|
||||||
override val serializersModule: SerializersModule = EmptySerializersModule
|
override val serializersModule: SerializersModule = EmptySerializersModule
|
||||||
override fun decodeBoolean(): Boolean = input.readByte().toInt() != 0
|
override fun decodeBoolean(): Boolean = input.readByte().toInt() != 0
|
||||||
override fun decodeByte(): Byte = input.readByte()
|
override fun decodeByte(): Byte = input.readByte()
|
||||||
override fun decodeShort(): Short = input.readNumber()
|
override fun decodeShort(): Short = if (nextIsUnsigned) input.readNumber<UInt>().toShort() else input.readNumber()
|
||||||
override fun decodeInt(): Int = input.readNumber()
|
override fun decodeInt(): Int = if (nextIsUnsigned) input.readNumber<UInt>().toInt() else input.readNumber()
|
||||||
override fun decodeLong(): Long = input.readNumber()
|
override fun decodeLong(): Long = 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())
|
||||||
@ -37,6 +39,7 @@ class BipackDecoder(val input: DataSource, var elementsCount: Int = 0) : Abstrac
|
|||||||
|
|
||||||
override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
|
override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
|
||||||
if (elementIndex >= elementsCount) return CompositeDecoder.DECODE_DONE
|
if (elementIndex >= elementsCount) return CompositeDecoder.DECODE_DONE
|
||||||
|
nextIsUnsigned = descriptor.getElementAnnotations(elementIndex).any { it is Unsigned }
|
||||||
return elementIndex++
|
return elementIndex++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,26 +14,49 @@ class BipackEncoder(var output: DataSink) : AbstractEncoder() {
|
|||||||
// used when CRC calculation on the fly
|
// used when CRC calculation on the fly
|
||||||
private var crcSink: CRC32Sink? = null
|
private var crcSink: CRC32Sink? = null
|
||||||
|
|
||||||
|
private var nextIsUnsigned = false
|
||||||
|
|
||||||
|
override fun encodeElement(descriptor: SerialDescriptor, index: Int): Boolean {
|
||||||
|
return super.encodeElement(descriptor, index).also {
|
||||||
|
println(">> $index: ${descriptor.getElementAnnotations(index)}")
|
||||||
|
nextIsUnsigned = descriptor.getElementAnnotations(index).any { it is Unsigned }
|
||||||
|
println("${descriptor.getElementDescriptor(index)} -> $nextIsUnsigned")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// fun isUnsigned(): Boolean {
|
||||||
|
//
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
override val serializersModule: SerializersModule = EmptySerializersModule
|
override val serializersModule: SerializersModule = EmptySerializersModule
|
||||||
override fun encodeBoolean(value: Boolean) = output.writeByte(if (value) 1 else 0)
|
override fun encodeBoolean(value: Boolean) = output.writeByte(if (value) 1 else 0)
|
||||||
override fun encodeByte(value: Byte) = output.writeByte(value.toInt())
|
override fun encodeByte(value: Byte) = output.writeByte(value.toInt())
|
||||||
override fun encodeShort(value: Short) = output.writeNumber(value.toInt())
|
override fun encodeShort(value: Short) = if( nextIsUnsigned )
|
||||||
override fun encodeInt(value: Int) = output.writeNumber(value)
|
output.writeNumber(value.toUInt())
|
||||||
|
else
|
||||||
|
output.writeNumber(value.toInt())
|
||||||
|
override fun encodeInt(value: Int) {
|
||||||
|
println("EncodeInt: $value / $nextIsUnsigned")
|
||||||
|
if (nextIsUnsigned)
|
||||||
|
output.writeNumber(value.toUInt())
|
||||||
|
else
|
||||||
|
output.writeNumber(value)
|
||||||
|
}
|
||||||
|
|
||||||
fun encodeUInt(value: UInt) = output.writeNumber(value)
|
fun encodeUInt(value: UInt) = output.writeNumber(value)
|
||||||
override fun encodeLong(value: Long) = output.writeNumber(value)
|
override fun encodeLong(value: Long) = if( nextIsUnsigned )
|
||||||
override fun encodeFloat(value: Float) = output.writeNumber(value)
|
output.writeNumber(value.toULong())
|
||||||
override fun encodeDouble(value: Double) = output.writeI64(value.toRawBits())
|
else
|
||||||
|
output.writeNumber(value)
|
||||||
|
override fun encodeFloat(value: Float) = output.writeFloat(value)
|
||||||
|
override fun encodeDouble(value: Double) = output.writeDouble(value)
|
||||||
override fun encodeChar(value: Char) = output.writeNumber(value.code.toUInt())
|
override fun encodeChar(value: Char) = output.writeNumber(value.code.toUInt())
|
||||||
override fun encodeString(value: String) {
|
override fun encodeString(value: String) {
|
||||||
// output.writeUTF(value)
|
// output.writeUTF(value)
|
||||||
writeBytes(value.encodeToByteArray())
|
writeBytes(value.encodeToByteArray())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun encodeElement(descriptor: SerialDescriptor, index: Int): Boolean {
|
|
||||||
return super.encodeElement(descriptor, index)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun writeBytes(value: ByteArray) {
|
fun writeBytes(value: ByteArray) {
|
||||||
output.writeNumber(value.size.toUInt())
|
output.writeNumber(value.size.toUInt())
|
||||||
output.writeBytes(value)
|
output.writeBytes(value)
|
||||||
@ -48,19 +71,18 @@ class BipackEncoder(var output: DataSink) : AbstractEncoder() {
|
|||||||
|
|
||||||
override fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder {
|
override fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder {
|
||||||
// frame protection should start before anything else:
|
// frame protection should start before anything else:
|
||||||
if( descriptor.annotations.any { it is CrcProtected }) {
|
if (descriptor.annotations.any { it is CrcProtected }) {
|
||||||
crcSink = CRC32Sink(output).also {
|
crcSink = CRC32Sink(output).also {
|
||||||
output = it
|
output = it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// now it is safe to process anything else
|
// now it is safe to process anything else
|
||||||
for( a in descriptor.annotations) {
|
for (a in descriptor.annotations) {
|
||||||
if (a is Framed) {
|
if (a is Framed) {
|
||||||
output.writeU32(
|
output.writeU32(
|
||||||
CRC.crc32(descriptor.serialName.encodeToByteArray())
|
CRC.crc32(descriptor.serialName.encodeToByteArray())
|
||||||
)
|
)
|
||||||
}
|
} else if (a is ExtendableFormat) {
|
||||||
else if( a is ExtendableFormat) {
|
|
||||||
encodeUInt(descriptor.elementsCount.toUInt())
|
encodeUInt(descriptor.elementsCount.toUInt())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -80,16 +102,16 @@ class BipackEncoder(var output: DataSink) : AbstractEncoder() {
|
|||||||
override fun encodeNotNullMark() = encodeBoolean(true)
|
override fun encodeNotNullMark() = encodeBoolean(true)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun <T> encode(serializer: SerializationStrategy<T>, value: T,sink: DataSink) {
|
fun <T> encode(serializer: SerializationStrategy<T>, value: T, sink: DataSink) {
|
||||||
val encoder = BipackEncoder(sink)
|
val encoder = BipackEncoder(sink)
|
||||||
encoder.encodeSerializableValue(serializer, value)
|
encoder.encodeSerializableValue(serializer, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T> encode(serializer: SerializationStrategy<T>, value: T): ByteArray =
|
fun <T> encode(serializer: SerializationStrategy<T>, value: T): ByteArray =
|
||||||
ArrayDataSink().also { encode(serializer, value, it)}.toByteArray()
|
ArrayDataSink().also { encode(serializer, value, it) }.toByteArray()
|
||||||
|
|
||||||
inline fun <reified T> encode(value: T) = encode(serializer(), value)
|
inline fun <reified T> encode(value: T) = encode(serializer(), value)
|
||||||
inline fun <reified T> encode(value: T,sink: DataSink) = encode(serializer(), value, sink)
|
inline fun <reified T> encode(value: T, sink: DataSink) = encode(serializer(), value, sink)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,9 +26,27 @@ annotation class ExtendableFormat
|
|||||||
@SerialInfo
|
@SerialInfo
|
||||||
annotation class Framed
|
annotation class Framed
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow to CRC-protect structures (we suppose to use it with classes only). After the
|
||||||
|
* data block its CRC32 will be written and checked. It is memory-wise: it calculates CRC
|
||||||
|
* on the fly without buffering the data. If used with [Framed] and [ExtendableFormat] the extra
|
||||||
|
* data is protected too.
|
||||||
|
*
|
||||||
|
* __Common pitfalls__. When unpacking corrupted data protected this way, the not only [InvalidFrameCRCException]
|
||||||
|
* can be thrown. Actually, most often you will see [DataSource.EndOfData] exception
|
||||||
|
*/
|
||||||
@SerialInfo
|
@SerialInfo
|
||||||
annotation class CrcProtected
|
annotation class CrcProtected
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow marking data fields as being serialized as usnsigned (applyable also to signed fields lite Int, Long and
|
||||||
|
* Short, if you are shure they will not be negative). As unsigned types are not cully supported by kotlinx.serialization
|
||||||
|
* it is the conly way to tell the serialized to use more compact unsigned variable length encoding.
|
||||||
|
*/
|
||||||
|
@SerialInfo
|
||||||
|
@Target(AnnotationTarget.FIELD,AnnotationTarget.PROPERTY)
|
||||||
|
annotation class Unsigned
|
||||||
|
|
||||||
open class InvalidFrameException(reason: String) : Exception(reason)
|
open class InvalidFrameException(reason: String) : Exception(reason)
|
||||||
class InvalidFrameHeaderException(reason: String = "Frame header does not match") : InvalidFrameException(reason)
|
class InvalidFrameHeaderException(reason: String = "Frame header does not match") : InvalidFrameException(reason)
|
||||||
class InvalidFrameCRCException : InvalidFrameException("Checksum CRC32 failed")
|
class InvalidFrameCRCException : InvalidFrameException("Checksum CRC32 failed")
|
||||||
|
@ -64,4 +64,5 @@ class SmartintTest {
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -40,6 +40,17 @@ data class FoobarFP1(val bar: Int, val foo: Int,val other: Int = -1)
|
|||||||
|
|
||||||
class BipackEncoderTest {
|
class BipackEncoderTest {
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class FoobarSize(val i: Int)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSize() {
|
||||||
|
println(BipackEncoder.encode(17).toDump())
|
||||||
|
assertEquals(1, BipackEncoder.encode(17).size)
|
||||||
|
// assertEquals(1, BipackEncoder.encode(FoobarSize(17)).size)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun encodeSimple() {
|
fun encodeSimple() {
|
||||||
val a = Foobar1(42)//, "bum")
|
val a = Foobar1(42)//, "bum")
|
||||||
@ -87,13 +98,34 @@ class BipackEncoderTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class FBU(val u: UInt, val i: Int)
|
data class FBU(
|
||||||
|
@Unsigned
|
||||||
|
val u: UInt,
|
||||||
|
@Unsigned
|
||||||
|
val i: Int,
|
||||||
|
val k: UInt = 3u)
|
||||||
@Test
|
@Test
|
||||||
fun testByteArray() {
|
fun testByteArray() {
|
||||||
|
// val z = Foobar1(42)//, "bum")
|
||||||
|
// println(BipackEncoder.encode(z).toDump())
|
||||||
|
|
||||||
val x = byteArrayOf(1,2,3)
|
val x = byteArrayOf(1,2,3)
|
||||||
println(BipackEncoder.encode(x).toDump())
|
var d = BipackEncoder.encode(x)
|
||||||
println(BipackEncoder.encode("123").toDump())
|
println(d.toDump())
|
||||||
println(BipackEncoder.encode(FBU(3u, 3)).toDump())
|
assertEquals(0x0c, d[0])
|
||||||
// println(BipackEncoder.encode(1U).toDump())
|
|
||||||
|
d = BipackEncoder.encode("123")
|
||||||
|
println(d.toDump())
|
||||||
|
assertEquals(0x0c, d[0])
|
||||||
|
|
||||||
|
val f = FBU(3u, 3)
|
||||||
|
d = BipackEncoder.encode(f)
|
||||||
|
println(d.toDump())
|
||||||
|
assertEquals(0x0c, d[0])
|
||||||
|
assertEquals(0x0c, d[1])
|
||||||
|
// Signed encoding despite UInt type:
|
||||||
|
assertEquals(0x18, d[2])
|
||||||
|
|
||||||
|
assertEquals(f, BipackDecoder.decode(d))
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user