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) {
|
||||
when(value) {
|
||||
is Float -> writeFloat(value)
|
||||
is Double -> writeDouble(value)
|
||||
else -> Smartint.encode(value, this)
|
||||
}
|
||||
Smartint.encode(value, this)
|
||||
}
|
||||
|
||||
fun DataSink.writeI32(value: Int) {
|
||||
|
@ -13,7 +13,7 @@ interface DataSource {
|
||||
/**
|
||||
* 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
|
||||
|
||||
@ -51,3 +51,4 @@ inline fun <reified T : Any> DataSource.readNumber(): T = when(typeOf<T>()) {
|
||||
typeOf<Float>() -> readFloat() as T
|
||||
else -> Smartint.decode(this)
|
||||
}
|
||||
|
||||
|
@ -17,12 +17,14 @@ import net.sergeych.bintools.*
|
||||
class BipackDecoder(val input: DataSource, var elementsCount: Int = 0) : AbstractDecoder() {
|
||||
private var elementIndex = 0
|
||||
|
||||
private var nextIsUnsigned = false
|
||||
|
||||
override val serializersModule: SerializersModule = EmptySerializersModule
|
||||
override fun decodeBoolean(): Boolean = input.readByte().toInt() != 0
|
||||
override fun decodeByte(): Byte = input.readByte()
|
||||
override fun decodeShort(): Short = input.readNumber()
|
||||
override fun decodeInt(): Int = input.readNumber()
|
||||
override fun decodeLong(): Long = input.readNumber()
|
||||
override fun decodeShort(): Short = if (nextIsUnsigned) input.readNumber<UInt>().toShort() else input.readNumber()
|
||||
override fun decodeInt(): Int = if (nextIsUnsigned) input.readNumber<UInt>().toInt() else input.readNumber()
|
||||
override fun decodeLong(): Long = 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())
|
||||
@ -37,6 +39,7 @@ class BipackDecoder(val input: DataSource, var elementsCount: Int = 0) : Abstrac
|
||||
|
||||
override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
|
||||
if (elementIndex >= elementsCount) return CompositeDecoder.DECODE_DONE
|
||||
nextIsUnsigned = descriptor.getElementAnnotations(elementIndex).any { it is Unsigned }
|
||||
return elementIndex++
|
||||
}
|
||||
|
||||
|
@ -14,26 +14,49 @@ class BipackEncoder(var output: DataSink) : AbstractEncoder() {
|
||||
// used when CRC calculation on the fly
|
||||
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 fun encodeBoolean(value: Boolean) = output.writeByte(if (value) 1 else 0)
|
||||
override fun encodeByte(value: Byte) = output.writeByte(value.toInt())
|
||||
override fun encodeShort(value: Short) = output.writeNumber(value.toInt())
|
||||
override fun encodeInt(value: Int) = output.writeNumber(value)
|
||||
override fun encodeShort(value: Short) = if( nextIsUnsigned )
|
||||
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)
|
||||
override fun encodeLong(value: Long) = output.writeNumber(value)
|
||||
override fun encodeFloat(value: Float) = output.writeNumber(value)
|
||||
override fun encodeDouble(value: Double) = output.writeI64(value.toRawBits())
|
||||
override fun encodeLong(value: Long) = 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)
|
||||
override fun encodeChar(value: Char) = output.writeNumber(value.code.toUInt())
|
||||
override fun encodeString(value: String) {
|
||||
// output.writeUTF(value)
|
||||
writeBytes(value.encodeToByteArray())
|
||||
}
|
||||
|
||||
override fun encodeElement(descriptor: SerialDescriptor, index: Int): Boolean {
|
||||
return super.encodeElement(descriptor, index)
|
||||
}
|
||||
|
||||
fun writeBytes(value: ByteArray) {
|
||||
output.writeNumber(value.size.toUInt())
|
||||
output.writeBytes(value)
|
||||
@ -48,19 +71,18 @@ class BipackEncoder(var output: DataSink) : AbstractEncoder() {
|
||||
|
||||
override fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder {
|
||||
// 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 {
|
||||
output = it
|
||||
}
|
||||
}
|
||||
// now it is safe to process anything else
|
||||
for( a in descriptor.annotations) {
|
||||
for (a in descriptor.annotations) {
|
||||
if (a is Framed) {
|
||||
output.writeU32(
|
||||
CRC.crc32(descriptor.serialName.encodeToByteArray())
|
||||
)
|
||||
}
|
||||
else if( a is ExtendableFormat) {
|
||||
} else if (a is ExtendableFormat) {
|
||||
encodeUInt(descriptor.elementsCount.toUInt())
|
||||
}
|
||||
}
|
||||
@ -80,16 +102,16 @@ class BipackEncoder(var output: DataSink) : AbstractEncoder() {
|
||||
override fun encodeNotNullMark() = encodeBoolean(true)
|
||||
|
||||
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)
|
||||
encoder.encodeSerializableValue(serializer, value)
|
||||
}
|
||||
|
||||
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,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
|
||||
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
|
||||
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)
|
||||
class InvalidFrameHeaderException(reason: String = "Frame header does not match") : InvalidFrameException(reason)
|
||||
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 {
|
||||
|
||||
@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
|
||||
fun encodeSimple() {
|
||||
val a = Foobar1(42)//, "bum")
|
||||
@ -87,13 +98,34 @@ class BipackEncoderTest {
|
||||
}
|
||||
|
||||
@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
|
||||
fun testByteArray() {
|
||||
// val z = Foobar1(42)//, "bum")
|
||||
// println(BipackEncoder.encode(z).toDump())
|
||||
|
||||
val x = byteArrayOf(1,2,3)
|
||||
println(BipackEncoder.encode(x).toDump())
|
||||
println(BipackEncoder.encode("123").toDump())
|
||||
println(BipackEncoder.encode(FBU(3u, 3)).toDump())
|
||||
// println(BipackEncoder.encode(1U).toDump())
|
||||
var d = BipackEncoder.encode(x)
|
||||
println(d.toDump())
|
||||
assertEquals(0x0c, d[0])
|
||||
|
||||
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