Skip to content

Commit c3718a4

Browse files
authored
Merge pull request #5 from ProjectMapK/refactor_unsigned_numbers
Refactor for `unsigned numbers` deserialization.
2 parents 9b8807c + 0d19ede commit c3718a4

File tree

4 files changed

+76
-101
lines changed

4 files changed

+76
-101
lines changed

src/main/kotlin/com/fasterxml/jackson/module/kotlin/UnsignedNumbers.kt

Lines changed: 0 additions & 24 deletions
This file was deleted.

src/main/kotlin/com/fasterxml/jackson/module/kotlin/deser/deserializers/KotlinDeserializers.kt

Lines changed: 8 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,13 @@
11
package com.fasterxml.jackson.module.kotlin.deser.deserializers
22

33
import com.fasterxml.jackson.core.JsonParser
4-
import com.fasterxml.jackson.core.JsonToken.VALUE_NUMBER_INT
5-
import com.fasterxml.jackson.core.exc.InputCoercionException
64
import com.fasterxml.jackson.databind.BeanDescription
75
import com.fasterxml.jackson.databind.DeserializationConfig
86
import com.fasterxml.jackson.databind.DeserializationContext
97
import com.fasterxml.jackson.databind.JavaType
108
import com.fasterxml.jackson.databind.JsonDeserializer
119
import com.fasterxml.jackson.databind.deser.Deserializers
1210
import com.fasterxml.jackson.databind.deser.std.StdDeserializer
13-
import com.fasterxml.jackson.module.kotlin.asUByte
14-
import com.fasterxml.jackson.module.kotlin.asUInt
15-
import com.fasterxml.jackson.module.kotlin.asULong
16-
import com.fasterxml.jackson.module.kotlin.asUShort
1711

1812
object SequenceDeserializer : StdDeserializer<Sequence<*>>(Sequence::class.java) {
1913
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): Sequence<*> {
@@ -45,44 +39,24 @@ object RegexDeserializer : StdDeserializer<Regex>(Regex::class.java) {
4539
}
4640
}
4741

48-
object UByteDeserializer : StdDeserializer<UByte>(UByte::class.java) {
42+
internal object UByteDeserializer : StdDeserializer<UByte>(UByte::class.java) {
4943
override fun deserialize(p: JsonParser, ctxt: DeserializationContext) =
50-
p.shortValue.asUByte() ?: throw InputCoercionException(
51-
p,
52-
"Numeric value (${p.text}) out of range of UByte (0 - ${UByte.MAX_VALUE}).",
53-
VALUE_NUMBER_INT,
54-
UByte::class.java
55-
)
44+
UByteChecker.readWithRangeCheck(p, p.intValue)
5645
}
5746

58-
object UShortDeserializer : StdDeserializer<UShort>(UShort::class.java) {
47+
internal object UShortDeserializer : StdDeserializer<UShort>(UShort::class.java) {
5948
override fun deserialize(p: JsonParser, ctxt: DeserializationContext) =
60-
p.intValue.asUShort() ?: throw InputCoercionException(
61-
p,
62-
"Numeric value (${p.text}) out of range of UShort (0 - ${UShort.MAX_VALUE}).",
63-
VALUE_NUMBER_INT,
64-
UShort::class.java
65-
)
49+
UShortChecker.readWithRangeCheck(p, p.intValue)
6650
}
6751

68-
object UIntDeserializer : StdDeserializer<UInt>(UInt::class.java) {
52+
internal object UIntDeserializer : StdDeserializer<UInt>(UInt::class.java) {
6953
override fun deserialize(p: JsonParser, ctxt: DeserializationContext) =
70-
p.longValue.asUInt() ?: throw InputCoercionException(
71-
p,
72-
"Numeric value (${p.text}) out of range of UInt (0 - ${UInt.MAX_VALUE}).",
73-
VALUE_NUMBER_INT,
74-
UInt::class.java
75-
)
54+
UIntChecker.readWithRangeCheck(p, p.longValue)
7655
}
7756

78-
object ULongDeserializer : StdDeserializer<ULong>(ULong::class.java) {
57+
internal object ULongDeserializer : StdDeserializer<ULong>(ULong::class.java) {
7958
override fun deserialize(p: JsonParser, ctxt: DeserializationContext) =
80-
p.bigIntegerValue.asULong() ?: throw InputCoercionException(
81-
p,
82-
"Numeric value (${p.text}) out of range of ULong (0 - ${ULong.MAX_VALUE}).",
83-
VALUE_NUMBER_INT,
84-
ULong::class.java
85-
)
59+
ULongChecker.readWithRangeCheck(p, p.bigIntegerValue)
8660
}
8761

8862
internal class KotlinDeserializers : Deserializers.Base() {

src/main/kotlin/com/fasterxml/jackson/module/kotlin/deser/deserializers/KotlinKeyDeserializers.kt

Lines changed: 10 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,31 @@
11
package com.fasterxml.jackson.module.kotlin.deser.deserializers
22

3-
import com.fasterxml.jackson.core.JsonToken
4-
import com.fasterxml.jackson.core.exc.InputCoercionException
53
import com.fasterxml.jackson.databind.*
64
import com.fasterxml.jackson.databind.deser.std.StdKeyDeserializer
75
import com.fasterxml.jackson.databind.deser.std.StdKeyDeserializers
8-
import com.fasterxml.jackson.module.kotlin.asUByte
9-
import com.fasterxml.jackson.module.kotlin.asUInt
10-
import com.fasterxml.jackson.module.kotlin.asULong
11-
import com.fasterxml.jackson.module.kotlin.asUShort
6+
import java.math.BigInteger
127

138
// The reason why key is treated as nullable is to match the tentative behavior of StdKeyDeserializer.
149
// If StdKeyDeserializer is modified, need to modify this too.
1510

1611
internal object UByteKeyDeserializer : StdKeyDeserializer(TYPE_SHORT, UByte::class.java) {
17-
override fun deserializeKey(key: String?, ctxt: DeserializationContext): UByte? = super.deserializeKey(key, ctxt)
18-
?.let {
19-
(it as Short).asUByte() ?: throw InputCoercionException(
20-
null,
21-
"Numeric value (${key}) out of range of UByte (0 - ${UByte.MAX_VALUE}).",
22-
JsonToken.VALUE_NUMBER_INT,
23-
UByte::class.java
24-
)
25-
}
12+
override fun deserializeKey(key: String?, ctxt: DeserializationContext): UByte? =
13+
key?.let { UByteChecker.readWithRangeCheck(null, _parseInt(it)) }
2614
}
2715

2816
internal object UShortKeyDeserializer : StdKeyDeserializer(TYPE_INT, UShort::class.java) {
29-
override fun deserializeKey(key: String?, ctxt: DeserializationContext): UShort? = super.deserializeKey(key, ctxt)
30-
?.let {
31-
(it as Int).asUShort() ?: throw InputCoercionException(
32-
null,
33-
"Numeric value (${key}) out of range of UShort (0 - ${UShort.MAX_VALUE}).",
34-
JsonToken.VALUE_NUMBER_INT,
35-
UShort::class.java
36-
)
37-
}
17+
override fun deserializeKey(key: String?, ctxt: DeserializationContext): UShort? =
18+
key?.let { UShortChecker.readWithRangeCheck(null, _parseInt(it)) }
3819
}
3920

4021
internal object UIntKeyDeserializer : StdKeyDeserializer(TYPE_LONG, UInt::class.java) {
41-
override fun deserializeKey(key: String?, ctxt: DeserializationContext): UInt? = super.deserializeKey(key, ctxt)
42-
?.let {
43-
(it as Long).asUInt() ?: throw InputCoercionException(
44-
null,
45-
"Numeric value (${key}) out of range of UInt (0 - ${UInt.MAX_VALUE}).",
46-
JsonToken.VALUE_NUMBER_INT,
47-
UInt::class.java
48-
)
49-
}
22+
override fun deserializeKey(key: String?, ctxt: DeserializationContext): UInt? =
23+
key?.let { UIntChecker.readWithRangeCheck(null, _parseLong(it)) }
5024
}
5125

52-
// kind parameter is dummy.
53-
internal object ULongKeyDeserializer : StdKeyDeserializer(TYPE_LONG, ULong::class.java) {
54-
override fun deserializeKey(key: String?, ctxt: DeserializationContext): ULong? = key?.let {
55-
it.toBigInteger().asULong() ?: throw InputCoercionException(
56-
null,
57-
"Numeric value (${key}) out of range of ULong (0 - ${ULong.MAX_VALUE}).",
58-
JsonToken.VALUE_NUMBER_INT,
59-
ULong::class.java
60-
)
61-
}
26+
internal object ULongKeyDeserializer : StdKeyDeserializer(-1, ULong::class.java) {
27+
override fun deserializeKey(key: String?, ctxt: DeserializationContext): ULong? =
28+
key?.let { ULongChecker.readWithRangeCheck(null, BigInteger(it)) }
6229
}
6330

6431
internal object KotlinKeyDeserializers : StdKeyDeserializers() {
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package com.fasterxml.jackson.module.kotlin.deser.deserializers
2+
3+
import com.fasterxml.jackson.core.JsonParser
4+
import com.fasterxml.jackson.core.JsonToken
5+
import com.fasterxml.jackson.core.exc.InputCoercionException
6+
import java.math.BigInteger
7+
8+
internal sealed class UnsignedIntegerChecker<T : Comparable<T>, U : Comparable<U>> {
9+
abstract val clazz: Class<T>
10+
abstract val max: U
11+
abstract val min: U
12+
13+
abstract fun U.convert(): T
14+
15+
fun readWithRangeCheck(p: JsonParser?, value: U): T = value.takeIf { it in min..max }
16+
?.convert()
17+
?: throw InputCoercionException(
18+
p,
19+
"Numeric value (${value}) out of range of ${clazz.simpleName} (${min} - ${max}).",
20+
JsonToken.VALUE_NUMBER_INT,
21+
clazz
22+
)
23+
}
24+
25+
// Not Short because it offers little performance benefit and rather increases the risk of overflow.
26+
internal object UByteChecker : UnsignedIntegerChecker<UByte, Int>() {
27+
override val clazz: Class<UByte> = UByte::class.java
28+
override val max: Int = UByte.MAX_VALUE.toInt()
29+
override val min: Int = UByte.MIN_VALUE.toInt()
30+
31+
override fun Int.convert(): UByte = toUByte()
32+
}
33+
34+
internal object UShortChecker : UnsignedIntegerChecker<UShort, Int>() {
35+
override val clazz: Class<UShort> = UShort::class.java
36+
override val max: Int = UShort.MAX_VALUE.toInt()
37+
override val min: Int = UShort.MIN_VALUE.toInt()
38+
39+
override fun Int.convert(): UShort = toUShort()
40+
}
41+
42+
internal object UIntChecker : UnsignedIntegerChecker<UInt, Long>() {
43+
override val clazz: Class<UInt> = UInt::class.java
44+
override val max: Long = UInt.MAX_VALUE.toLong()
45+
override val min: Long = UInt.MIN_VALUE.toLong()
46+
47+
override fun Long.convert(): UInt = toUInt()
48+
}
49+
50+
internal object ULongChecker : UnsignedIntegerChecker<ULong, BigInteger>() {
51+
override val clazz: Class<ULong> = ULong::class.java
52+
override val max: BigInteger = BigInteger(ULong.MAX_VALUE.toString())
53+
override val min: BigInteger = BigInteger.ZERO
54+
55+
// Since BigInteger.toLong returns a negative value on overflow,
56+
// it can be converted directly to ULong after range check.
57+
override fun BigInteger.convert(): ULong = toLong().toULong()
58+
}

0 commit comments

Comments
 (0)